Compare commits
22 Commits
extension-
...
reactify-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18fb2a90f9 | ||
|
|
d1226152a7 | ||
|
|
7dcc5fb58e | ||
|
|
d69d573e65 | ||
|
|
528191e8da | ||
|
|
07aea4cdf0 | ||
|
|
3eaf290a58 | ||
|
|
c276009bd8 | ||
|
|
ea93d4604c | ||
|
|
caf41de755 | ||
|
|
45d80e9440 | ||
|
|
bb8f11963a | ||
|
|
6581d1424e | ||
|
|
253b298b0d | ||
|
|
df554680c4 | ||
|
|
92032cf1eb | ||
|
|
22cd81611d | ||
|
|
1a3e468458 | ||
|
|
88962d4380 | ||
|
|
3b1285aeaf | ||
|
|
b73e244865 | ||
|
|
5f050282f0 |
@@ -17,6 +17,13 @@ if($group == 'admin' || $group == 'modules'){
|
|||||||
$name = str_replace("..","",$name);
|
$name = str_replace("..","",$name);
|
||||||
$name = str_replace("/","",$name);
|
$name = str_replace("/","",$name);
|
||||||
include APP_BASE_PATH.'/'.$group.'/'.$name.'/index.php';
|
include APP_BASE_PATH.'/'.$group.'/'.$name.'/index.php';
|
||||||
|
}else if ($group == 'extension'){
|
||||||
|
$name = str_replace("..","",$name);
|
||||||
|
$name = str_replace("/","",$name);
|
||||||
|
$moduleName = $name;
|
||||||
|
$moduleGroup = 'extensions';
|
||||||
|
$extensionIndex = APP_BASE_PATH.'/../extensions/'.$name.'/web/index.php';
|
||||||
|
include $extensionIndex;
|
||||||
}else{
|
}else{
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,9 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Clients\Common\Model\Client;
|
||||||
|
|
||||||
$moduleName = 'clients';
|
$moduleName = 'clients';
|
||||||
$moduleGroup = 'admin';
|
$moduleGroup = 'admin';
|
||||||
define('MODULE_PATH', dirname(__FILE__));
|
define('MODULE_PATH', dirname(__FILE__));
|
||||||
@@ -17,35 +20,32 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="tabPageClient">
|
<div class="tab-pane active" id="tabPageClient">
|
||||||
<div id="Client" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="ClientTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="ClientForm"></div>
|
||||||
</div>
|
<div id="ClientFilterForm"></div>
|
||||||
<div id="ClientForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'Client' => PermissionManager::checkGeneralAccess(new Client()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
<script>
|
<script>
|
||||||
|
var data = <?= json_encode($moduleData) ?>;
|
||||||
var modJsList = [];
|
var modJsList = [];
|
||||||
|
|
||||||
modJsList['tabClient'] = new ClientAdapter('Client','Client');
|
modJsList['tabClient'] = new ClientAdapter('Client');
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Add Clients']) && $modulePermissions['perm']['Add Clients'] == "No"){?>
|
|
||||||
modJsList['tabClient'].setShowAddNew(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Delete Clients']) && $modulePermissions['perm']['Delete Clients'] == "No"){?>
|
|
||||||
modJsList['tabClient'].setShowDelete(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Edit Clients']) && $modulePermissions['perm']['Edit Clients'] == "No"){?>
|
|
||||||
modJsList['tabClient'].setShowSave(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
|
modJsList.tabClient.setObjectTypeName('Client');
|
||||||
|
modJsList.tabClient.setAccess(data.permissions.Client);
|
||||||
|
modJsList.tabClient.setDataPipe(new IceDataPipe(modJsList.tabClient));
|
||||||
|
modJsList.tabClient.setRemoteTable(true);
|
||||||
|
|
||||||
var modJs = modJsList['tabClient'];
|
var modJs = modJsList['tabClient'];
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<?php include APP_BASE_PATH . 'footer.php'; ?>
|
<?php include APP_BASE_PATH . 'footer.php'; ?>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\BaseService;
|
||||||
use Classes\PermissionManager;
|
use Classes\PermissionManager;
|
||||||
use Company\Common\Model\CompanyStructure;
|
use Company\Common\Model\CompanyStructure;
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ path.link {
|
|||||||
<?php
|
<?php
|
||||||
$moduleData = [
|
$moduleData = [
|
||||||
'user_level' => $user->user_level,
|
'user_level' => $user->user_level,
|
||||||
|
'customFields' => BaseService::getInstance()->getCustomFields("CompanyStructure"),
|
||||||
'permissions' => [
|
'permissions' => [
|
||||||
'CompanyStructure' => PermissionManager::checkGeneralAccess(new CompanyStructure()),
|
'CompanyStructure' => PermissionManager::checkGeneralAccess(new CompanyStructure()),
|
||||||
]
|
]
|
||||||
|
|||||||
42
core/admin/custom_fields/index.php
Normal file
42
core/admin/custom_fields/index.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Classes\BaseService;
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use FieldNames\Common\Model\CustomField;
|
||||||
|
|
||||||
|
$moduleName = 'assets';
|
||||||
|
$moduleGroup = 'admin';
|
||||||
|
define('MODULE_PATH',dirname(__FILE__));
|
||||||
|
include APP_BASE_PATH.'header.php';
|
||||||
|
include APP_BASE_PATH.'modulejslibs.inc.php';
|
||||||
|
$modelClasses = BaseService::getInstance()->getCustomFieldClassMap();
|
||||||
|
?><div class="span9">
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||||
|
<li class="active"><a id="tabCustomField" href="#tabPageCustomField"><?=t('Custom Fields')?></a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="tabPageCustomField">
|
||||||
|
<div id="CustomFieldTable" class="reviewBlock" data-content="List"></div>
|
||||||
|
<div id="CustomFieldForm"></div>
|
||||||
|
<div id="CustomFieldFilterForm"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="dataGroup"></div>
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'types' => $modelClasses,
|
||||||
|
'permissions' => [
|
||||||
|
'CustomField' => PermissionManager::checkGeneralAccess(new CustomField()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
initAdminCustomFields(<?=json_encode($moduleData)?>);
|
||||||
|
</script>
|
||||||
|
<?php include APP_BASE_PATH.'footer.php';?>
|
||||||
|
|
||||||
12
core/admin/custom_fields/meta.json
Normal file
12
core/admin/custom_fields/meta.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"label": "Custom Fields",
|
||||||
|
"menu": "Admin",
|
||||||
|
"order": "892",
|
||||||
|
"icon": "fa-code",
|
||||||
|
"user_levels": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"permissions": [],
|
||||||
|
"model_namespace": "\\FieldNames\\Common\\Model",
|
||||||
|
"manager": "\\CustomField\\Admin\\Api\\CustomFieldAdminManager"
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||||
<li class="active"><a id="tabEmployeeFieldName" href="#tabPageEmployeeFieldName"><?=t('Employee Field Names')?></a></li>
|
<li class="active"><a id="tabEmployeeFieldName" href="#tabPageEmployeeFieldName"><?=t('Employee Field Names')?></a></li>
|
||||||
<li><a id="tabEmployeeCustomField" href="#tabPageEmployeeCustomField"><?=t('Employee Custom Fields')?></a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
@@ -25,29 +24,17 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="tabPageEmployeeCustomField">
|
|
||||||
<div id="EmployeeCustomField" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="EmployeeCustomFieldForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
var modJsList = new Array();
|
var modJsList = [];
|
||||||
|
|
||||||
modJsList['tabEmployeeFieldName'] = new FieldNameAdapter('FieldNameMapping','EmployeeFieldName',{"type":"Employee"});
|
modJsList['tabEmployeeFieldName'] = new FieldNameAdapter('FieldNameMapping','EmployeeFieldName',{"type":"Employee"});
|
||||||
modJsList['tabEmployeeFieldName'].setRemoteTable(true);
|
modJsList['tabEmployeeFieldName'].setRemoteTable(true);
|
||||||
modJsList['tabEmployeeFieldName'].setShowDelete(false);
|
modJsList['tabEmployeeFieldName'].setShowDelete(false);
|
||||||
modJsList['tabEmployeeFieldName'].setShowAddNew(false);
|
modJsList['tabEmployeeFieldName'].setShowAddNew(false);
|
||||||
|
|
||||||
modJsList['tabEmployeeCustomField'] = new CustomFieldAdapter('CustomField','EmployeeCustomField',{"type":"Employee"},"display_order desc");
|
|
||||||
modJsList['tabEmployeeCustomField'].setRemoteTable(true);
|
|
||||||
modJsList['tabEmployeeCustomField'].setTableType("Employee");
|
|
||||||
|
|
||||||
|
|
||||||
var modJs = modJsList['tabEmployeeFieldName'];
|
var modJs = modJsList['tabEmployeeFieldName'];
|
||||||
|
|
||||||
|
|||||||
@@ -28,20 +28,23 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
<div id="JobTitleForm"></div>
|
<div id="JobTitleForm"></div>
|
||||||
<div id="JobTitleFilterForm"></div>
|
<div id="JobTitleFilterForm"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane" id="tabPagePayGrades">
|
<div class="tab-pane" id="tabPagePayGrades">
|
||||||
<div id="PayGradeTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
<div id="PayGradeTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
<div id="PayGradeForm"></div>
|
<div id="PayGradeForm"></div>
|
||||||
<div id="PayGradeFilterForm"></div>
|
<div id="PayGradeFilterForm"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane" id="tabPageEmploymentStatus">
|
<div class="tab-pane" id="tabPageEmploymentStatus">
|
||||||
<div id="EmploymentStatusTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
<div id="EmploymentStatusTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
<div id="EmploymentStatusForm"></div>
|
<div id="EmploymentStatusForm"></div>
|
||||||
<div id="EmploymentStatusFilterForm"></div>
|
<div id="EmploymentStatusFilterForm"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="dataGroup"></div>
|
|
||||||
<?php
|
<?php
|
||||||
$moduleData = [
|
$moduleData = [
|
||||||
'user_level' => $user->user_level,
|
'user_level' => $user->user_level,
|
||||||
|
|||||||
@@ -4,12 +4,17 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Loans\Common\Model\CompanyLoan;
|
||||||
|
use Loans\Common\Model\EmployeeCompanyLoan;
|
||||||
|
|
||||||
$moduleName = 'loans';
|
$moduleName = 'loans';
|
||||||
$moduleGroup = 'admin';
|
$moduleGroup = 'admin';
|
||||||
define('MODULE_PATH',dirname(__FILE__));
|
define('MODULE_PATH',dirname(__FILE__));
|
||||||
include APP_BASE_PATH.'header.php';
|
include APP_BASE_PATH.'header.php';
|
||||||
include APP_BASE_PATH.'modulejslibs.inc.php';
|
include APP_BASE_PATH.'modulejslibs.inc.php';
|
||||||
?><div class="span9">
|
?>
|
||||||
|
<div class="span9">
|
||||||
|
|
||||||
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||||
<li class="active"><a id="tabCompanyLoan" href="#tabPageCompanyLoan"><?=t('Loan Types')?></a></li>
|
<li class="active"><a id="tabCompanyLoan" href="#tabPageCompanyLoan"><?=t('Loan Types')?></a></li>
|
||||||
@@ -18,31 +23,32 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="tabPageCompanyLoan">
|
<div class="tab-pane active" id="tabPageCompanyLoan">
|
||||||
<div id="CompanyLoan" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="CompanyLoanTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="CompanyLoanForm"></div>
|
||||||
|
<div id="CompanyLoanFilterForm"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="CompanyLoanForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="tab-pane" id="tabPageEmployeeCompanyLoan">
|
<div class="tab-pane" id="tabPageEmployeeCompanyLoan">
|
||||||
<div id="EmployeeCompanyLoan" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="EmployeeCompanyLoanTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="EmployeeCompanyLoanForm"></div>
|
||||||
</div>
|
<div id="EmployeeCompanyLoanFilterForm"></div>
|
||||||
<div id="EmployeeCompanyLoanForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'CompanyLoan' => PermissionManager::checkGeneralAccess(new CompanyLoan()),
|
||||||
|
'EmployeeCompanyLoan' => PermissionManager::checkGeneralAccess(new EmployeeCompanyLoan()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var modJsList = new Array();
|
initAdminLoan(<?=json_encode($moduleData)?>);
|
||||||
|
|
||||||
modJsList['tabCompanyLoan'] = new CompanyLoanAdapter('CompanyLoan','CompanyLoan');
|
|
||||||
modJsList['tabEmployeeCompanyLoan'] = new EmployeeCompanyLoanAdapter('EmployeeCompanyLoan','EmployeeCompanyLoan');
|
|
||||||
|
|
||||||
var modJs = modJsList['tabCompanyLoan'];
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php include APP_BASE_PATH.'footer.php';?>
|
<?php include APP_BASE_PATH.'footer.php';?>
|
||||||
|
|||||||
@@ -4,13 +4,19 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Projects\Common\Model\Project;
|
||||||
|
use Projects\Common\Model\EmployeeProject;
|
||||||
|
|
||||||
$moduleName = 'projects';
|
$moduleName = 'projects';
|
||||||
$moduleGroup = 'admin';
|
$moduleGroup = 'admin';
|
||||||
define('MODULE_PATH', dirname(__FILE__));
|
define('MODULE_PATH', dirname(__FILE__));
|
||||||
include APP_BASE_PATH . 'header.php';
|
include APP_BASE_PATH . 'header.php';
|
||||||
include APP_BASE_PATH . 'modulejslibs.inc.php';
|
include APP_BASE_PATH . 'modulejslibs.inc.php';
|
||||||
|
|
||||||
?><div class="span9">
|
?><div class="span9">
|
||||||
|
|
||||||
|
|
||||||
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||||
<li class="active"><a id="tabProject" href="#tabPageProject"><?= t('Projects') ?></a></li>
|
<li class="active"><a id="tabProject" href="#tabPageProject"><?= t('Projects') ?></a></li>
|
||||||
<li><a id="tabEmployeeProject" href="#tabPageEmployeeProject"><?= t('Employee Projects') ?></a></li>
|
<li><a id="tabEmployeeProject" href="#tabPageEmployeeProject"><?= t('Employee Projects') ?></a></li>
|
||||||
@@ -18,55 +24,43 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="tabPageProject">
|
<div class="tab-pane active" id="tabPageProject">
|
||||||
<div id="Project" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="ProjectTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="ProjectForm"></div>
|
||||||
</div>
|
<div id="ProjectFilterForm"></div>
|
||||||
<div id="ProjectForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="tabPageEmployeeProject">
|
<div class="tab-pane" id="tabPageEmployeeProject">
|
||||||
<div id="EmployeeProject" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="EmployeeProjectTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="EmployeeProjectForm"></div>
|
||||||
</div>
|
<div id="EmployeeProjectFilterForm"></div>
|
||||||
<div id="EmployeeProjectForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'Project' => PermissionManager::checkGeneralAccess(new Project()),
|
||||||
|
'EmployeeProject' => PermissionManager::checkGeneralAccess(new EmployeeProject()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
<script>
|
<script>
|
||||||
var modJsList = [];
|
var modJsList = [];
|
||||||
|
var data = <?= json_encode($moduleData) ?>;
|
||||||
modJsList['tabProject'] = new ProjectAdapter('Project', 'Project');
|
modJsList['tabProject'] = new ProjectAdapter('Project', 'Project');
|
||||||
|
modJsList.tabProject.setObjectTypeName('Project');
|
||||||
<?php if(isset($modulePermissions['perm']['Add Projects']) && $modulePermissions['perm']['Add Projects'] == "No"){?>
|
modJsList.tabProject.setAccess(data.permissions.Project);
|
||||||
modJsList['tabProject'].setShowAddNew(false);
|
modJsList.tabProject.setDataPipe(new IceDataPipe(modJsList.tabProject));
|
||||||
<?php }?>
|
modJsList.tabProject.setRemoteTable(true);
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Delete Projects']) && $modulePermissions['perm']['Delete Projects'] == "No"){?>
|
|
||||||
modJsList['tabProject'].setShowDelete(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Edit Projects']) && $modulePermissions['perm']['Edit Projects'] == "No"){?>
|
|
||||||
modJsList['tabProject'].setShowSave(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
|
|
||||||
modJsList['tabEmployeeProject'] = new EmployeeProjectAdapter('EmployeeProject', 'EmployeeProject');
|
modJsList['tabEmployeeProject'] = new EmployeeProjectAdapter('EmployeeProject', 'EmployeeProject');
|
||||||
modJsList['tabEmployeeProject'].setRemoteTable(true);
|
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Add Projects']) && $modulePermissions['perm']['Add Projects'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeProject'].setShowAddNew(false);
|
|
||||||
<?php }?>
|
|
||||||
<?php if(isset($modulePermissions['perm']['Delete Projects']) && $modulePermissions['perm']['Delete Projects'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeProject'].setShowDelete(false);
|
|
||||||
<?php }?>
|
|
||||||
<?php if(isset($modulePermissions['perm']['Edit Projects']) && $modulePermissions['perm']['Edit Projects'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeProject'].setShowEdit(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
|
modJsList.tabEmployeeProject.setObjectTypeName('Employee Project');
|
||||||
|
modJsList.tabEmployeeProject.setAccess(data.permissions.EmployeeProject);
|
||||||
|
modJsList.tabEmployeeProject.setDataPipe(new IceDataPipe(modJsList.tabEmployeeProject));
|
||||||
|
modJsList.tabEmployeeProject.setRemoteTable(true);
|
||||||
|
|
||||||
var modJs = modJsList['tabProject'];
|
var modJs = modJsList['tabProject'];
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Salary\Common\Model\SalaryComponentType;
|
||||||
|
use Salary\Common\Model\SalaryComponent;
|
||||||
|
use Salary\Common\Model\EmployeeSalary;
|
||||||
|
|
||||||
|
|
||||||
$moduleName = 'salary';
|
$moduleName = 'salary';
|
||||||
$moduleGroup = 'admin';
|
$moduleGroup = 'admin';
|
||||||
@@ -6,7 +11,7 @@ define('MODULE_PATH',dirname(__FILE__));
|
|||||||
include APP_BASE_PATH.'header.php';
|
include APP_BASE_PATH.'header.php';
|
||||||
include APP_BASE_PATH.'modulejslibs.inc.php';
|
include APP_BASE_PATH.'modulejslibs.inc.php';
|
||||||
|
|
||||||
$moduleBuilder = new \Classes\ModuleBuilder\ModuleBuilder();
|
/*$moduleBuilder = new \Classes\ModuleBuilder\ModuleBuilder();
|
||||||
|
|
||||||
$moduleBuilder->addModuleOrGroup(new \Classes\ModuleBuilder\ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new \Classes\ModuleBuilder\ModuleTab(
|
||||||
'SalaryComponentType','SalaryComponentType','Salary Component Types','SalaryComponentTypeAdapter','','',true
|
'SalaryComponentType','SalaryComponentType','Salary Component Types','SalaryComponentTypeAdapter','','',true
|
||||||
@@ -21,4 +26,55 @@ $moduleBuilder->addModuleOrGroup(new \Classes\ModuleBuilder\ModuleTab(
|
|||||||
|
|
||||||
echo \Classes\UIManager::getInstance()->renderModule($moduleBuilder);
|
echo \Classes\UIManager::getInstance()->renderModule($moduleBuilder);
|
||||||
|
|
||||||
include APP_BASE_PATH.'footer.php';
|
include APP_BASE_PATH.'footer.php';*/
|
||||||
|
|
||||||
|
?><div class="span9">
|
||||||
|
|
||||||
|
<ul class="nav nav-tabs" id="modTab" style="margin-bottom:0px;margin-left:5px;border-bottom: none;">
|
||||||
|
<li class="active"><a id="tabSalaryComponentType" href="#tabSalaryComponentType"><?=t('Salary Component Types')?></a></li>
|
||||||
|
<li><a id="tabSalaryComponent" href="#tabPageSalaryComponent"><?=t('Salary Components')?></a></li>
|
||||||
|
<li><a id="tabEmployeeSalary" href="#tabPageEmployeeSalary"><?=t('Employee Salary Components')?></a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" id="tabPageSalaryComponentType">
|
||||||
|
<div id="SalaryComponentTypeTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="SalaryComponentTypeForm"></div>
|
||||||
|
<div id="SalaryComponentTypeFilterForm"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane" id="tabPageSalaryComponent">
|
||||||
|
<div id="SalaryComponentTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="SalaryComponentForm"></div>
|
||||||
|
<div id="SalaryComponentFilterForm"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane" id="tabPageEmployeeSalary">
|
||||||
|
<div id="EmployeeSalaryTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="EmployeeSalaryForm"></div>
|
||||||
|
<div id="EmployeeSalaryFilterForm"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'SalaryComponentType' => PermissionManager::checkGeneralAccess(new SalaryComponentType()),
|
||||||
|
'SalaryComponent' => PermissionManager::checkGeneralAccess(new SalaryComponent()),
|
||||||
|
'EmployeeSalary' => PermissionManager::checkGeneralAccess(new EmployeeSalary()),
|
||||||
|
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
initAdminSalary(<?=json_encode($moduleData)?>);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<?php include APP_BASE_PATH.'footer.php';?>
|
||||||
|
|||||||
@@ -19,28 +19,42 @@ $options1 = array();
|
|||||||
$options1['setShowAddNew'] = 'false';
|
$options1['setShowAddNew'] = 'false';
|
||||||
$options1['setRemoteTable'] = 'true';
|
$options1['setRemoteTable'] = 'true';
|
||||||
|
|
||||||
|
$notCloud = !defined('IS_CLOUD') || IS_CLOUD == false;
|
||||||
|
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'CompanySetting','Setting','Company','SettingAdapter','{"category":"Company"}','name',true,$options1
|
'CompanySetting','Setting','Company','SettingAdapter','{"category":"Company"}','name',true,$options1
|
||||||
));
|
));
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'SystemSetting','Setting','System','SettingAdapter','{"category":"System"}','name',false,$options1
|
'SystemSetting','Setting','System','SettingAdapter','{"category":"System"}','name',false,$options1
|
||||||
));
|
));
|
||||||
if (!defined('CLOUD_INSTALLATION')) {
|
if ( $notCloud ) {
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'EmailSetting', 'Setting', 'Email', 'SettingAdapter', '{"category":"Email"}', 'name', false, $options1
|
'EmailSetting', 'Setting', 'Email', 'SettingAdapter', '{"category":"Email"}', 'name', false, $options1
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if(!defined('LEAVE_ENABLED') || LDAP_ENABLED == true) {
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'LeaveSetting','Setting','Leave / PTO','SettingAdapter','{"category":"Leave"}','name',false,$options1
|
'LeaveSetting', 'Setting', 'Leave', 'SettingAdapter', '{"category":"Leave"}', 'name', false, $options1
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
|
'AttendanceSetting','Setting','Attendance','SettingAdapter','{"category":"Attendance"}','name',false,$options1
|
||||||
|
));
|
||||||
|
|
||||||
if(!defined('LDAP_ENABLED') || LDAP_ENABLED == true){
|
if(!defined('LDAP_ENABLED') || LDAP_ENABLED == true){
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'LDAPSetting','Setting','LDAP','SettingAdapter','{"category":"LDAP"}','name',false,$options1
|
'LDAPSetting','Setting','LDAP','SettingAdapter','{"category":"LDAP"}','name',false,$options1
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!defined('SAML_ENABLED') || SAML_ENABLED == true){
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'AttendanceSetting','Setting','Attendance','SettingAdapter','{"category":"Attendance"}','name',false,$options1
|
'SAMLSetting','Setting','SAML','SettingAdapter','{"category":"SAML"}','name',false,$options1
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
$moduleBuilder->addModuleOrGroup(new ModuleTab(
|
||||||
'OtherSetting','Setting','Other','SettingAdapter','{"category":["Projects","Recruitment","Notifications","Expense","Travel","Api","Overtime"]}','name',false,$options1
|
'OtherSetting','Setting','Other','SettingAdapter','{"category":["Projects","Recruitment","Notifications","Expense","Travel","Api","Overtime"]}','name',false,$options1
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -31,22 +31,6 @@ $moduleBuilder->addModuleOrGroup(new \Classes\ModuleBuilder\ModuleTab(
|
|||||||
$travelRequestOptions
|
$travelRequestOptions
|
||||||
));
|
));
|
||||||
|
|
||||||
if ($user->user_level === 'Admin') {
|
|
||||||
$travelCustomFieldOptions = [];
|
|
||||||
$travelCustomFieldOptions['setRemoteTable'] = 'true';
|
|
||||||
$travelCustomFieldOptions['setTableType'] = '\'EmployeeTravelRecord\'';
|
|
||||||
|
|
||||||
$moduleBuilder->addModuleOrGroup(new \Classes\ModuleBuilder\ModuleTab(
|
|
||||||
'TravelCustomField',
|
|
||||||
'CustomField',
|
|
||||||
'Custom Fields',
|
|
||||||
'CustomFieldAdapter',
|
|
||||||
'{"type":"EmployeeTravelRecord"}',
|
|
||||||
'',
|
|
||||||
false,
|
|
||||||
$travelCustomFieldOptions
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
echo \Classes\UIManager::getInstance()->renderModule($moduleBuilder);
|
echo \Classes\UIManager::getInstance()->renderModule($moduleBuilder);
|
||||||
|
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ if(!defined('HOME_LINK_OTHERS')){
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Version
|
//Version
|
||||||
define('VERSION', '28.1.1.OS');
|
define('VERSION', '29.0.0.OS');
|
||||||
define('CACHE_VALUE', '28.1.1.OS.2020-11071143');
|
define('CACHE_VALUE', '29.0.0.OS.2020-04021509');
|
||||||
define('VERSION_NUMBER', '280101');
|
define('VERSION_NUMBER', '290000');
|
||||||
define('VERSION_DATE', '07/11/2020');
|
define('VERSION_DATE', '02/04/2021');
|
||||||
|
|
||||||
if(!defined('CONTACT_EMAIL')){define('CONTACT_EMAIL','icehrm@gamonoid.com');}
|
if(!defined('CONTACT_EMAIL')){define('CONTACT_EMAIL','icehrm@gamonoid.com');}
|
||||||
if(!defined('KEY_PREFIX')){define('KEY_PREFIX','IceHrm');}
|
if(!defined('KEY_PREFIX')){define('KEY_PREFIX','IceHrm');}
|
||||||
if(!defined('APP_SEC')){define('APP_SEC','dbcs234d2saaqw');}
|
|
||||||
|
|
||||||
define('UI_SHOW_SWITCH_PROFILE', true);
|
define('UI_SHOW_SWITCH_PROFILE', true);
|
||||||
define('CRON_LOG', ini_get('error_log'));
|
define('CRON_LOG', ini_get('error_log'));
|
||||||
@@ -33,6 +32,13 @@ if(!defined('WK_HTML_PATH')){
|
|||||||
}
|
}
|
||||||
define('ALL_CLIENT_BASE_PATH', '/var/www/icehrm.app/icehrmapp/');
|
define('ALL_CLIENT_BASE_PATH', '/var/www/icehrm.app/icehrmapp/');
|
||||||
|
|
||||||
|
define('IS_CLOUD', false);
|
||||||
define('LDAP_ENABLED', true);
|
define('LDAP_ENABLED', true);
|
||||||
|
define('SAML_ENABLED', true);
|
||||||
|
define('LEAVE_ENABLED', true);
|
||||||
define('RECRUITMENT_ENABLED', false);
|
define('RECRUITMENT_ENABLED', false);
|
||||||
define('APP_WEB_URL', 'https://icehrm.com');
|
define('APP_WEB_URL', 'https://icehrm.com');
|
||||||
|
|
||||||
|
if (!defined('EXTENSIONS_URL')) {
|
||||||
|
define('EXTENSIONS_URL', str_replace('/web/', '/extensions/', BASE_URL));
|
||||||
|
}
|
||||||
|
|||||||
@@ -215,6 +215,26 @@ if (defined('SYM_CLIENT')) {
|
|||||||
|
|
||||||
<?php }?>
|
<?php }?>
|
||||||
|
|
||||||
|
<?php foreach($extensions as $menu){?>
|
||||||
|
<?php if(count($menu['menu']) == 0){continue;}?>
|
||||||
|
<li class="treeview" ref="<?="extension_".str_replace(" ", "_", $menu['name'])?>">
|
||||||
|
<a href="#">
|
||||||
|
<i class="fa <?=!isset($mainIcons[$menu['name']])?"fa-th":$mainIcons[$menu['name']];?>"></i></i> <span><?=\Classes\LanguageManager::tran($menu['name'])?></span>
|
||||||
|
<i class="fa fa-angle-left pull-right"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul class="treeview-menu" id="<?="extension_".str_replace(" ", "_", $menu['name'])?>">
|
||||||
|
<?php foreach ($menu['menu'] as $item){?>
|
||||||
|
<li>
|
||||||
|
<a data-turbolinks="true" href="<?=CLIENT_BASE_URL?>?g=extension&n=<?=$item['name']?>&m=<?="extension_".str_replace(" ", "_", $menu['name'])?>">
|
||||||
|
<i class="fa <?=!isset($item['icon'])?"fa-angle-double-right":$item['icon']?>"></i> <?=\Classes\LanguageManager::tran($item['label'])?>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<?php }?>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<?php }?>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
if(file_exists(CLIENT_PATH.'/third_party_meta.json')){
|
if(file_exists(CLIENT_PATH.'/third_party_meta.json')){
|
||||||
$tpModules = json_decode(file_get_contents(CLIENT_PATH.'/third_party_meta.json'),true);
|
$tpModules = json_decode(file_get_contents(CLIENT_PATH.'/third_party_meta.json'),true);
|
||||||
|
|||||||
1354
core/lib/saml2/Assertion.php
Normal file
1354
core/lib/saml2/Assertion.php
Normal file
File diff suppressed because it is too large
Load Diff
185
core/lib/saml2/MetadataReader.php
Normal file
185
core/lib/saml2/MetadataReader.php
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
include_once 'Utilities.php';
|
||||||
|
class IDPMetadataReader{
|
||||||
|
|
||||||
|
private $identityProviders;
|
||||||
|
private $serviceProviders;
|
||||||
|
|
||||||
|
public function __construct(DOMNode $xml = NULL){
|
||||||
|
|
||||||
|
$this->identityProviders = array();
|
||||||
|
$this->serviceProviders = array();
|
||||||
|
|
||||||
|
$entitiesDescriptor = Utilities::xpQuery($xml, './saml_metadata:EntitiesDescriptor');
|
||||||
|
|
||||||
|
if(!empty($entitiesDescriptor))
|
||||||
|
$entityDescriptors = Utilities::xpQuery($entitiesDescriptor[0], './saml_metadata:EntityDescriptor');
|
||||||
|
else
|
||||||
|
$entityDescriptors = Utilities::xpQuery($xml, './saml_metadata:EntityDescriptor');
|
||||||
|
|
||||||
|
foreach ($entityDescriptors as $entityDescriptor) {
|
||||||
|
$idpSSODescriptor = Utilities::xpQuery($entityDescriptor, './saml_metadata:IDPSSODescriptor');
|
||||||
|
|
||||||
|
if(isset($idpSSODescriptor) && !empty($idpSSODescriptor)){
|
||||||
|
array_push($this->identityProviders,new IdentityProviders($entityDescriptor));
|
||||||
|
}
|
||||||
|
//TODO: add sp descriptor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdentityProviders(){
|
||||||
|
return $this->identityProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceProviders(){
|
||||||
|
return $this->serviceProviders;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdentityProviders{
|
||||||
|
|
||||||
|
private $idpName;
|
||||||
|
private $entityID;
|
||||||
|
private $loginDetails;
|
||||||
|
private $logoutDetails;
|
||||||
|
private $signingCertificate;
|
||||||
|
private $encryptionCertificate;
|
||||||
|
private $signedRequest;
|
||||||
|
|
||||||
|
public function __construct(DOMElement $xml = NULL){
|
||||||
|
|
||||||
|
$this->idpName = '';
|
||||||
|
$this->loginDetails = array();
|
||||||
|
$this->logoutDetails = array();
|
||||||
|
$this->signingCertificate = array();
|
||||||
|
$this->encryptionCertificate = array();
|
||||||
|
|
||||||
|
if ($xml->hasAttribute('entityID')) {
|
||||||
|
$this->entityID = $xml->getAttribute('entityID');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($xml->hasAttribute('WantAuthnRequestsSigned')){
|
||||||
|
$this->signedRequest = $xml->getAttribute('WantAuthnRequestsSigned');
|
||||||
|
}
|
||||||
|
|
||||||
|
$idpSSODescriptor = Utilities::xpQuery($xml, './saml_metadata:IDPSSODescriptor');
|
||||||
|
|
||||||
|
if (count($idpSSODescriptor) > 1) {
|
||||||
|
throw new Exception('More than one <IDPSSODescriptor> in <EntityDescriptor>.');
|
||||||
|
} elseif (empty($idpSSODescriptor)) {
|
||||||
|
throw new Exception('Missing required <IDPSSODescriptor> in <EntityDescriptor>.');
|
||||||
|
}
|
||||||
|
$idpSSODescriptorEL = $idpSSODescriptor[0];
|
||||||
|
|
||||||
|
$info = Utilities::xpQuery($xml, './saml_metadata:Extensions');
|
||||||
|
|
||||||
|
if($info)
|
||||||
|
$this->parseInfo($idpSSODescriptorEL);
|
||||||
|
$this->parseSSOService($idpSSODescriptorEL);
|
||||||
|
$this->parseSLOService($idpSSODescriptorEL);
|
||||||
|
$this->parsex509Certificate($idpSSODescriptorEL);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseInfo($xml){
|
||||||
|
$displayNames = Utilities::xpQuery($xml, './mdui:UIInfo/mdui:DisplayName');
|
||||||
|
foreach ($displayNames as $name) {
|
||||||
|
if($name->hasAttribute('xml:lang') && $name->getAttribute('xml:lang')=="en"){
|
||||||
|
$this->idpName = $name->textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseSSOService($xml){
|
||||||
|
$ssoServices = Utilities::xpQuery($xml, './saml_metadata:SingleSignOnService');
|
||||||
|
foreach ($ssoServices as $ssoService) {
|
||||||
|
$binding = str_replace("urn:oasis:names:tc:SAML:2.0:bindings:","",$ssoService->getAttribute('Binding'));
|
||||||
|
$this->loginDetails = array_merge(
|
||||||
|
$this->loginDetails,
|
||||||
|
array($binding => $ssoService->getAttribute('Location'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseSLOService($xml){
|
||||||
|
$sloServices = Utilities::xpQuery($xml, './saml_metadata:SingleLogoutService');
|
||||||
|
foreach ($sloServices as $sloService) {
|
||||||
|
$binding = str_replace("urn:oasis:names:tc:SAML:2.0:bindings:","",$sloService->getAttribute('Binding'));
|
||||||
|
$this->logoutDetails = array_merge(
|
||||||
|
$this->logoutDetails,
|
||||||
|
array($binding => $sloService->getAttribute('Location'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parsex509Certificate($xml){
|
||||||
|
foreach ( Utilities::xpQuery($xml, './saml_metadata:KeyDescriptor') as $KeyDescriptorNode ) {
|
||||||
|
if($KeyDescriptorNode->hasAttribute('use')){
|
||||||
|
if($KeyDescriptorNode->getAttribute('use')=='encryption'){
|
||||||
|
$this->parseEncryptionCertificate($KeyDescriptorNode);
|
||||||
|
}else{
|
||||||
|
$this->parseSigningCertificate($KeyDescriptorNode);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$this->parseSigningCertificate($KeyDescriptorNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function parseSigningCertificate($xml){
|
||||||
|
$certNode = Utilities::xpQuery($xml, './ds:KeyInfo/ds:X509Data/ds:X509Certificate');
|
||||||
|
$certData = trim($certNode[0]->textContent);
|
||||||
|
$certData = str_replace(array ( "\r", "\n", "\t", ' '), '', $certData);
|
||||||
|
if(!empty($certNode))
|
||||||
|
array_push($this->signingCertificate, Utilities::sanitize_certificate( $certData ));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private function parseEncryptionCertificate($xml){
|
||||||
|
$certNode = Utilities::xpQuery($xml, './ds:KeyInfo/ds:X509Data/ds:X509Certificate');
|
||||||
|
$certData = trim($certNode[0]->textContent);
|
||||||
|
$certData = str_replace(array ( "\r", "\n", "\t", ' '), '', $certData);
|
||||||
|
if(!empty($certNode))
|
||||||
|
array_push($this->encryptionCertificate, $certData);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIdpName(){
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEntityID(){
|
||||||
|
return $this->entityID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLoginURL($binding){
|
||||||
|
return $this->loginDetails[$binding];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogoutURL($binding){
|
||||||
|
return $this->logoutDetails[$binding];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLoginDetails(){
|
||||||
|
return $this->loginDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLogoutDetails(){
|
||||||
|
return $this->logoutDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSigningCertificate(){
|
||||||
|
return $this->signingCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEncryptionCertificate(){
|
||||||
|
return $this->encryptionCertificate[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRequestSigned(){
|
||||||
|
return $this->signedRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
33
core/lib/saml2/MoSAMLBasicEnum.php
Normal file
33
core/lib/saml2/MoSAMLBasicEnum.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class MoSAMLBasicEnum {
|
||||||
|
private static $constCacheArray = NULL;
|
||||||
|
|
||||||
|
public static function getConstants() {
|
||||||
|
if (self::$constCacheArray == NULL) {
|
||||||
|
self::$constCacheArray = array();
|
||||||
|
}
|
||||||
|
$calledClass = get_called_class();
|
||||||
|
if (!array_key_exists($calledClass, self::$constCacheArray)) {
|
||||||
|
$reflect = new ReflectionClass($calledClass);
|
||||||
|
self::$constCacheArray[$calledClass] = $reflect->getConstants();
|
||||||
|
}
|
||||||
|
return self::$constCacheArray[$calledClass];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidName($name, $strict = false) {
|
||||||
|
$constants = self::getConstants();
|
||||||
|
|
||||||
|
if ($strict) {
|
||||||
|
return array_key_exists($name, $constants);
|
||||||
|
}
|
||||||
|
|
||||||
|
$keys = array_map('strtolower', array_keys($constants));
|
||||||
|
return in_array(strtolower($name), $keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function isValidValue($value, $strict = true) {
|
||||||
|
$values = array_values(self::getConstants());
|
||||||
|
return in_array($value, $values, $strict);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
core/lib/saml2/MoSAMLPointer.php
Normal file
109
core/lib/saml2/MoSAMLPointer.php
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: HP
|
||||||
|
* Date: 9/11/2018
|
||||||
|
* Time: 9:48 AM
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MoSAMLPointer
|
||||||
|
{
|
||||||
|
|
||||||
|
private $content,$anchor_id,$edge,$align,$active,$pointer_name;
|
||||||
|
|
||||||
|
function __construct($header,$body,$anchor_id,$edge,$align,$active,$prefix){
|
||||||
|
|
||||||
|
$this->content = '<h3>'.$header.'</h3>';
|
||||||
|
$this->content .= '<p id="'.$prefix.'" style="font-size: initial;">' .$body . '</p>';
|
||||||
|
$this-> anchor_id = $anchor_id;
|
||||||
|
$this->edge = $edge;
|
||||||
|
$this->align = $align;
|
||||||
|
$this->active = $active;
|
||||||
|
$this->pointer_name = 'miniorange_admin_pointer_'.$prefix;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function return_array(){
|
||||||
|
return array(
|
||||||
|
// The content needs to point to what we created above in the $new_pointer_content variable
|
||||||
|
'content' => $this->content,
|
||||||
|
|
||||||
|
// In order for the custom pointer to appear in the right location we need to specify the ID
|
||||||
|
// of the element we want it to appear next to
|
||||||
|
'anchor_id' => $this->anchor_id,
|
||||||
|
|
||||||
|
// On what edge do we want the pointer to appear. Options are 'top', 'left', 'right', 'bottom'
|
||||||
|
'edge' => $this->edge,
|
||||||
|
|
||||||
|
// How do we want out custom pointer to align to the element it is attached to. Options are
|
||||||
|
// 'left', 'right', 'center'
|
||||||
|
'align' => $this->align,
|
||||||
|
|
||||||
|
// This is how we tell the pointer to be dismissed or not. Make sure that the 'new_items'
|
||||||
|
// string matches the string at the beginning of the array item
|
||||||
|
'active' => $this->active
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $content
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAnchorId()
|
||||||
|
{
|
||||||
|
return $this->anchor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEdge()
|
||||||
|
{
|
||||||
|
return $this->edge;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getActive()
|
||||||
|
{
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $active
|
||||||
|
*/
|
||||||
|
public function setActive($active)
|
||||||
|
{
|
||||||
|
$this->active = $active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getPointerName()
|
||||||
|
{
|
||||||
|
return $this->pointer_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
51
core/lib/saml2/PointersManager.php
Normal file
51
core/lib/saml2/PointersManager.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class MoSAMLPointersManager {
|
||||||
|
|
||||||
|
private $pfile;
|
||||||
|
private $version;
|
||||||
|
private $prefix;
|
||||||
|
private $pointers = array();
|
||||||
|
|
||||||
|
public function __construct( $file, $version, $prefix ) {
|
||||||
|
$this->pfile = file_exists( $file ) ? $file : FALSE;
|
||||||
|
$this->version = str_replace( '.', '_', $version );
|
||||||
|
$this->prefix = $prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parse() {
|
||||||
|
if ( empty( $this->pfile ) ) return;
|
||||||
|
$pointers = (array) require_once $this->pfile;
|
||||||
|
if ( empty($pointers) ) return;
|
||||||
|
foreach ( $pointers as $i => $pointer ) {
|
||||||
|
if(is_array($pointer)){
|
||||||
|
$pointer['id'] = "{$this->prefix}{$this->version}_{$i}";
|
||||||
|
$this->pointers[$pointer['id']] = (object) $pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function filter( $page ) {
|
||||||
|
if ( empty( $this->pointers ) ) return array();
|
||||||
|
$uid = get_current_user_id();
|
||||||
|
$no = explode( ',', (string) get_user_meta( $uid, 'dismissed_wp_pointers', TRUE ) );
|
||||||
|
$active_ids = array_diff( array_keys( $this->pointers ), $no );
|
||||||
|
$good = array();
|
||||||
|
foreach( $this->pointers as $i => $pointer ) {
|
||||||
|
if (
|
||||||
|
in_array( $i, $active_ids, TRUE ) // is active
|
||||||
|
&& isset( $pointer->where ) // has where
|
||||||
|
&& in_array( $page, (array) $pointer->where, TRUE ) // current page is in where
|
||||||
|
) {
|
||||||
|
$good[] = $pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$count = count( $good );
|
||||||
|
if ( $good === 0 ) return array();
|
||||||
|
foreach( array_values( $good ) as $i => $pointer ) {
|
||||||
|
$good[$i]->next = $i+1 < $count ? $good[$i+1]->id : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $good;
|
||||||
|
}
|
||||||
|
}
|
||||||
114
core/lib/saml2/Response.php
Normal file
114
core/lib/saml2/Response.php
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of miniOrange SAML plugin.
|
||||||
|
*
|
||||||
|
* miniOrange SAML plugin is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* miniOrange SAML plugin is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with miniOrange SAML plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include 'Assertion.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for SAML2 Response messages.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class SAML2_Response
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The assertions in this response.
|
||||||
|
*/
|
||||||
|
private $assertions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The destination URL in this response.
|
||||||
|
*/
|
||||||
|
private $destination;
|
||||||
|
|
||||||
|
private $certificates;
|
||||||
|
private $signatureData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for SAML 2 response messages.
|
||||||
|
*
|
||||||
|
* @param DOMElement|NULL $xml The input message.
|
||||||
|
*/
|
||||||
|
public function __construct(DOMElement $xml = NULL)
|
||||||
|
{
|
||||||
|
//parent::__construct('Response', $xml);
|
||||||
|
|
||||||
|
$this->assertions = array();
|
||||||
|
$this->certificates = array();
|
||||||
|
|
||||||
|
if ($xml === NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sig = Utilities::validateElement($xml);
|
||||||
|
if ($sig !== FALSE) {
|
||||||
|
$this->certificates = $sig['Certificates'];
|
||||||
|
$this->signatureData = $sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set the destination from saml response */
|
||||||
|
if ($xml->hasAttribute('Destination')) {
|
||||||
|
$this->destination = $xml->getAttribute('Destination');
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($node = $xml->firstChild; $node !== NULL; $node = $node->nextSibling) {
|
||||||
|
if ($node->namespaceURI !== 'urn:oasis:names:tc:SAML:2.0:assertion') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($node->localName === 'Assertion' || $node->localName === 'EncryptedAssertion') {
|
||||||
|
$this->assertions[] = new SAML2_Assertion($node);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the assertions in this response.
|
||||||
|
*
|
||||||
|
* @return SAML2_Assertion[]|SAML2_EncryptedAssertion[]
|
||||||
|
*/
|
||||||
|
public function getAssertions()
|
||||||
|
{
|
||||||
|
return $this->assertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the assertions that should be included in this response.
|
||||||
|
*
|
||||||
|
* @param SAML2_Assertion[]|SAML2_EncryptedAssertion[] The assertions.
|
||||||
|
*/
|
||||||
|
public function setAssertions(array $assertions)
|
||||||
|
{
|
||||||
|
$this->assertions = $assertions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDestination()
|
||||||
|
{
|
||||||
|
return $this->destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getCertificates()
|
||||||
|
{
|
||||||
|
return $this->certificates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSignatureData()
|
||||||
|
{
|
||||||
|
return $this->signatureData;
|
||||||
|
}
|
||||||
|
}
|
||||||
511
core/lib/saml2/SAML2Core/MoXMLSecEnc.php
Normal file
511
core/lib/saml2/SAML2Core/MoXMLSecEnc.php
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
<?php
|
||||||
|
namespace RobRichards\XMLSecLibs;
|
||||||
|
|
||||||
|
use DOMDocument;
|
||||||
|
use DOMElement;
|
||||||
|
use DOMNode;
|
||||||
|
use DOMXPath;
|
||||||
|
use Exception;
|
||||||
|
use RobRichards\XMLSecLibs\Utils\MoXPath as XPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlseclibs.php
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Robert Richards nor the names of his
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @author Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MoXMLSecEnc
|
||||||
|
{
|
||||||
|
const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
|
||||||
|
<xenc:CipherData>
|
||||||
|
<xenc:CipherValue></xenc:CipherValue>
|
||||||
|
</xenc:CipherData>
|
||||||
|
</xenc:EncryptedData>";
|
||||||
|
|
||||||
|
const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
|
||||||
|
const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
|
||||||
|
const URI = 3;
|
||||||
|
const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
|
||||||
|
|
||||||
|
/** @var null|DOMDocument */
|
||||||
|
private $encdoc = null;
|
||||||
|
|
||||||
|
/** @var null|DOMNode */
|
||||||
|
private $rawNode = null;
|
||||||
|
|
||||||
|
/** @var null|string */
|
||||||
|
public $type = null;
|
||||||
|
|
||||||
|
/** @var null|DOMElement */
|
||||||
|
public $encKey = null;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $references = array();
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->_resetTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _resetTemplate()
|
||||||
|
{
|
||||||
|
$this->encdoc = new DOMDocument();
|
||||||
|
$this->encdoc->loadXML(self::template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @param DOMNode $node
|
||||||
|
* @param string $type
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function addReference($name, $node, $type)
|
||||||
|
{
|
||||||
|
if (! $node instanceOf DOMNode) {
|
||||||
|
throw new Exception('$node is not of type DOMNode');
|
||||||
|
}
|
||||||
|
$curencdoc = $this->encdoc;
|
||||||
|
$this->_resetTemplate();
|
||||||
|
$encdoc = $this->encdoc;
|
||||||
|
$this->encdoc = $curencdoc;
|
||||||
|
$refuri = MoXMLSecurityDSig::generateGUID();
|
||||||
|
$element = $encdoc->documentElement;
|
||||||
|
$element->setAttribute("Id", $refuri);
|
||||||
|
$this->references[$name] = array("node" => $node, "type" => $type, "encnode" => $encdoc, "refuri" => $refuri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DOMNode $node
|
||||||
|
*/
|
||||||
|
public function setNode($node)
|
||||||
|
{
|
||||||
|
$this->rawNode = $node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the selected node with the given key.
|
||||||
|
*
|
||||||
|
* @param MoXMLSecurityKey $objKey The encryption key and algorithm.
|
||||||
|
* @param bool $replace Whether the encrypted node should be replaced in the original tree. Default is true.
|
||||||
|
* @return DOMElement The <xenc:EncryptedData>-element.
|
||||||
|
*@throws Exception
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function encryptNode($objKey, $replace = true)
|
||||||
|
{
|
||||||
|
$data = '';
|
||||||
|
if (empty($this->rawNode)) {
|
||||||
|
throw new Exception('Node to encrypt has not been set');
|
||||||
|
}
|
||||||
|
if (! $objKey instanceof MoXMLSecurityKey) {
|
||||||
|
throw new Exception('Invalid Key');
|
||||||
|
}
|
||||||
|
$doc = $this->rawNode->ownerDocument;
|
||||||
|
$xPath = new DOMXPath($this->encdoc);
|
||||||
|
$objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
|
||||||
|
$cipherValue = $objList->item(0);
|
||||||
|
if ($cipherValue == null) {
|
||||||
|
throw new Exception('Error locating CipherValue element within template');
|
||||||
|
}
|
||||||
|
switch ($this->type) {
|
||||||
|
case (self::Element):
|
||||||
|
$data = $doc->saveXML($this->rawNode);
|
||||||
|
$this->encdoc->documentElement->setAttribute('Type', self::Element);
|
||||||
|
break;
|
||||||
|
case (self::Content):
|
||||||
|
$children = $this->rawNode->childNodes;
|
||||||
|
foreach ($children AS $child) {
|
||||||
|
$data .= $doc->saveXML($child);
|
||||||
|
}
|
||||||
|
$this->encdoc->documentElement->setAttribute('Type', self::Content);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception('Type is currently not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
$encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod'));
|
||||||
|
$encMethod->setAttribute('Algorithm', $objKey->getAlgorithm());
|
||||||
|
$cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode->parentNode->firstChild);
|
||||||
|
|
||||||
|
$strEncrypt = base64_encode($objKey->encryptData($data));
|
||||||
|
$value = $this->encdoc->createTextNode($strEncrypt);
|
||||||
|
$cipherValue->appendChild($value);
|
||||||
|
|
||||||
|
if ($replace) {
|
||||||
|
switch ($this->type) {
|
||||||
|
case (self::Element):
|
||||||
|
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
|
||||||
|
return $this->encdoc;
|
||||||
|
}
|
||||||
|
$importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true);
|
||||||
|
$this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
|
||||||
|
return $importEnc;
|
||||||
|
case (self::Content):
|
||||||
|
$importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true);
|
||||||
|
while ($this->rawNode->firstChild) {
|
||||||
|
$this->rawNode->removeChild($this->rawNode->firstChild);
|
||||||
|
}
|
||||||
|
$this->rawNode->appendChild($importEnc);
|
||||||
|
return $importEnc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $this->encdoc->documentElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param MoXMLSecurityKey $objKey
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function encryptReferences($objKey)
|
||||||
|
{
|
||||||
|
$curRawNode = $this->rawNode;
|
||||||
|
$curType = $this->type;
|
||||||
|
foreach ($this->references AS $name => $reference) {
|
||||||
|
$this->encdoc = $reference["encnode"];
|
||||||
|
$this->rawNode = $reference["node"];
|
||||||
|
$this->type = $reference["type"];
|
||||||
|
try {
|
||||||
|
$encNode = $this->encryptNode($objKey);
|
||||||
|
$this->references[$name]["encnode"] = $encNode;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$this->rawNode = $curRawNode;
|
||||||
|
$this->type = $curType;
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->rawNode = $curRawNode;
|
||||||
|
$this->type = $curType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the CipherValue text from this encrypted node.
|
||||||
|
*
|
||||||
|
* @throws Exception
|
||||||
|
* @return string|null The Ciphervalue text, or null if no CipherValue is found.
|
||||||
|
*/
|
||||||
|
public function getCipherValue()
|
||||||
|
{
|
||||||
|
if (empty($this->rawNode)) {
|
||||||
|
throw new Exception('Node to decrypt has not been set');
|
||||||
|
}
|
||||||
|
|
||||||
|
$doc = $this->rawNode->ownerDocument;
|
||||||
|
$xPath = new DOMXPath($doc);
|
||||||
|
$xPath->registerNamespace('xmlencr', self::XMLENCNS);
|
||||||
|
/* Only handles embedded content right now and not a reference */
|
||||||
|
$query = "./xmlencr:CipherData/xmlencr:CipherValue";
|
||||||
|
$nodeset = $xPath->query($query, $this->rawNode);
|
||||||
|
$node = $nodeset->item(0);
|
||||||
|
|
||||||
|
if (!$node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base64_decode($node->nodeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt this encrypted node.
|
||||||
|
*
|
||||||
|
* The behaviour of this function depends on the value of $replace.
|
||||||
|
* If $replace is false, we will return the decrypted data as a string.
|
||||||
|
* If $replace is true, we will insert the decrypted element(s) into the
|
||||||
|
* document, and return the decrypted element(s).
|
||||||
|
*
|
||||||
|
* @param MoXMLSecurityKey $objKey The decryption key that should be used when decrypting the node.
|
||||||
|
* @param boolean $replace Whether we should replace the encrypted node in the XML document with the decrypted data. The default is true.
|
||||||
|
*
|
||||||
|
* @return string|DOMElement The decrypted data.
|
||||||
|
*/
|
||||||
|
public function decryptNode($objKey, $replace=true)
|
||||||
|
{
|
||||||
|
if (! $objKey instanceof MoXMLSecurityKey) {
|
||||||
|
throw new Exception('Invalid Key');
|
||||||
|
}
|
||||||
|
|
||||||
|
$encryptedData = $this->getCipherValue();
|
||||||
|
if ($encryptedData) {
|
||||||
|
$decrypted = $objKey->decryptData($encryptedData);
|
||||||
|
if ($replace) {
|
||||||
|
switch ($this->type) {
|
||||||
|
case (self::Element):
|
||||||
|
$newdoc = new DOMDocument();
|
||||||
|
$newdoc->loadXML($decrypted);
|
||||||
|
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
|
||||||
|
return $newdoc;
|
||||||
|
}
|
||||||
|
$importEnc = $this->rawNode->ownerDocument->importNode($newdoc->documentElement, true);
|
||||||
|
$this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
|
||||||
|
return $importEnc;
|
||||||
|
case (self::Content):
|
||||||
|
if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
|
||||||
|
$doc = $this->rawNode;
|
||||||
|
} else {
|
||||||
|
$doc = $this->rawNode->ownerDocument;
|
||||||
|
}
|
||||||
|
$newFrag = $doc->createDocumentFragment();
|
||||||
|
$newFrag->appendXML($decrypted);
|
||||||
|
$parent = $this->rawNode->parentNode;
|
||||||
|
$parent->replaceChild($newFrag, $this->rawNode);
|
||||||
|
return $parent;
|
||||||
|
default:
|
||||||
|
return $decrypted;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $decrypted;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Exception("Cannot locate encrypted data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt the XMLSecurityKey
|
||||||
|
*
|
||||||
|
* @param MoXMLSecurityKey $srcKey
|
||||||
|
* @param MoXMLSecurityKey $rawKey
|
||||||
|
* @param bool $append
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function encryptKey($srcKey, $rawKey, $append=true)
|
||||||
|
{
|
||||||
|
if ((! $srcKey instanceof MoXMLSecurityKey) || (! $rawKey instanceof MoXMLSecurityKey)) {
|
||||||
|
throw new Exception('Invalid Key');
|
||||||
|
}
|
||||||
|
$strEncKey = base64_encode($srcKey->encryptData($rawKey->key));
|
||||||
|
$root = $this->encdoc->documentElement;
|
||||||
|
$encKey = $this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptedKey');
|
||||||
|
if ($append) {
|
||||||
|
$keyInfo = $root->insertBefore($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'), $root->firstChild);
|
||||||
|
$keyInfo->appendChild($encKey);
|
||||||
|
} else {
|
||||||
|
$this->encKey = $encKey;
|
||||||
|
}
|
||||||
|
$encMethod = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod'));
|
||||||
|
$encMethod->setAttribute('Algorithm', $srcKey->getAlgorith());
|
||||||
|
if (! empty($srcKey->name)) {
|
||||||
|
$keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
|
||||||
|
$keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name));
|
||||||
|
}
|
||||||
|
$cipherData = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherData'));
|
||||||
|
$cipherData->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherValue', $strEncKey));
|
||||||
|
if (is_array($this->references) && count($this->references) > 0) {
|
||||||
|
$refList = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:ReferenceList'));
|
||||||
|
foreach ($this->references AS $name => $reference) {
|
||||||
|
$refuri = $reference["refuri"];
|
||||||
|
$dataRef = $refList->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:DataReference'));
|
||||||
|
$dataRef->setAttribute("URI", '#' . $refuri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param MoXMLSecurityKey $encKey
|
||||||
|
* @return DOMElement|string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function decryptKey($encKey)
|
||||||
|
{
|
||||||
|
if (! $encKey->isEncrypted) {
|
||||||
|
throw new Exception("Key is not Encrypted");
|
||||||
|
}
|
||||||
|
if (empty($encKey->key)) {
|
||||||
|
throw new Exception("Key is missing data to perform the decryption");
|
||||||
|
}
|
||||||
|
return $this->decryptNode($encKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param DOMDocument $element
|
||||||
|
* @return DOMNode|null
|
||||||
|
*/
|
||||||
|
public function locateEncryptedData($element)
|
||||||
|
{
|
||||||
|
if ($element instanceof DOMDocument) {
|
||||||
|
$doc = $element;
|
||||||
|
} else {
|
||||||
|
$doc = $element->ownerDocument;
|
||||||
|
}
|
||||||
|
if ($doc) {
|
||||||
|
$xpath = new DOMXPath($doc);
|
||||||
|
$query = "//*[local-name()='EncryptedData' and namespace-uri()='".self::XMLENCNS."']";
|
||||||
|
$nodeset = $xpath->query($query);
|
||||||
|
return $nodeset->item(0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key from the DOM
|
||||||
|
* @param null|DOMNode $node
|
||||||
|
* @return null|MoXMLSecurityKey
|
||||||
|
*/
|
||||||
|
public function locateKey($node=null)
|
||||||
|
{
|
||||||
|
if (empty($node)) {
|
||||||
|
$node = $this->rawNode;
|
||||||
|
}
|
||||||
|
if (! $node instanceof DOMNode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ($doc = $node->ownerDocument) {
|
||||||
|
$xpath = new DOMXPath($doc);
|
||||||
|
$xpath->registerNamespace('xmlsecenc', self::XMLENCNS);
|
||||||
|
$query = ".//xmlsecenc:EncryptionMethod";
|
||||||
|
$nodeset = $xpath->query($query, $node);
|
||||||
|
if ($encmeth = $nodeset->item(0)) {
|
||||||
|
$attrAlgorithm = $encmeth->getAttribute("Algorithm");
|
||||||
|
try {
|
||||||
|
$objKey = new MoXMLSecurityKey($attrAlgorithm, array('type' => 'private'));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $objKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null|MoXMLSecurityKey $objBaseKey
|
||||||
|
* @param null|DOMNode $node
|
||||||
|
* @return null|MoXMLSecurityKey
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function staticLocateKeyInfo($objBaseKey=null, $node=null)
|
||||||
|
{
|
||||||
|
if (empty($node) || (! $node instanceof DOMNode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
if (!$doc) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$xpath = new DOMXPath($doc);
|
||||||
|
$xpath->registerNamespace('xmlsecenc', self::XMLENCNS);
|
||||||
|
$xpath->registerNamespace('xmlsecdsig', MoXMLSecurityDSig::XMLDSIGNS);
|
||||||
|
$query = "./xmlsecdsig:KeyInfo";
|
||||||
|
$nodeset = $xpath->query($query, $node);
|
||||||
|
$encmeth = $nodeset->item(0);
|
||||||
|
if (!$encmeth) {
|
||||||
|
/* No KeyInfo in EncryptedData / EncryptedKey. */
|
||||||
|
return $objBaseKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($encmeth->childNodes AS $child) {
|
||||||
|
switch ($child->localName) {
|
||||||
|
case 'KeyName':
|
||||||
|
if (! empty($objBaseKey)) {
|
||||||
|
$objBaseKey->name = $child->nodeValue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'KeyValue':
|
||||||
|
foreach ($child->childNodes AS $keyval) {
|
||||||
|
switch ($keyval->localName) {
|
||||||
|
case 'DSAKeyValue':
|
||||||
|
throw new Exception("DSAKeyValue currently not supported");
|
||||||
|
case 'RSAKeyValue':
|
||||||
|
$modulus = null;
|
||||||
|
$exponent = null;
|
||||||
|
if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) {
|
||||||
|
$modulus = base64_decode($modulusNode->nodeValue);
|
||||||
|
}
|
||||||
|
if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) {
|
||||||
|
$exponent = base64_decode($exponentNode->nodeValue);
|
||||||
|
}
|
||||||
|
if (empty($modulus) || empty($exponent)) {
|
||||||
|
throw new Exception("Missing Modulus or Exponent");
|
||||||
|
}
|
||||||
|
$publicKey = MoXMLSecurityKey::convertRSA($modulus, $exponent);
|
||||||
|
$objBaseKey->loadKey($publicKey);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'RetrievalMethod':
|
||||||
|
$type = $child->getAttribute('Type');
|
||||||
|
if ($type !== 'http://www.w3.org/2001/04/xmlenc#EncryptedKey') {
|
||||||
|
/* Unsupported key type. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$uri = $child->getAttribute('URI');
|
||||||
|
if ($uri[0] !== '#') {
|
||||||
|
/* URI not a reference - unsupported. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$id = substr($uri, 1);
|
||||||
|
|
||||||
|
$query = '//xmlsecenc:EncryptedKey[@Id="'.MoXPath::filterAttrValue($id, MoXPath::DOUBLE_QUOTE).'"]';
|
||||||
|
$keyElement = $xpath->query($query)->item(0);
|
||||||
|
if (!$keyElement) {
|
||||||
|
throw new Exception("Unable to locate EncryptedKey with @Id='$id'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MoXMLSecurityKey::fromEncryptedKeyElement($keyElement);
|
||||||
|
case 'EncryptedKey':
|
||||||
|
return MoXMLSecurityKey::fromEncryptedKeyElement($child);
|
||||||
|
case 'X509Data':
|
||||||
|
if ($x509certNodes = $child->getElementsByTagName('X509Certificate')) {
|
||||||
|
if ($x509certNodes->length > 0) {
|
||||||
|
$x509cert = $x509certNodes->item(0)->textContent;
|
||||||
|
$x509cert = str_replace(array("\r", "\n", " "), "", $x509cert);
|
||||||
|
$x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n";
|
||||||
|
$objBaseKey->loadKey($x509cert, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $objBaseKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param null|MoXMLSecurityKey $objBaseKey
|
||||||
|
* @param null|DOMNode $node
|
||||||
|
* @return null|MoXMLSecurityKey
|
||||||
|
*/
|
||||||
|
public function locateKeyInfo($objBaseKey=null, $node=null)
|
||||||
|
{
|
||||||
|
if (empty($node)) {
|
||||||
|
$node = $this->rawNode;
|
||||||
|
}
|
||||||
|
return self::staticLocateKeyInfo($objBaseKey, $node);
|
||||||
|
}
|
||||||
|
}
|
||||||
1162
core/lib/saml2/SAML2Core/MoXMLSecurityDSig.php
Normal file
1162
core/lib/saml2/SAML2Core/MoXMLSecurityDSig.php
Normal file
File diff suppressed because it is too large
Load Diff
800
core/lib/saml2/SAML2Core/MoXMLSecurityKey.php
Normal file
800
core/lib/saml2/SAML2Core/MoXMLSecurityKey.php
Normal file
@@ -0,0 +1,800 @@
|
|||||||
|
<?php
|
||||||
|
namespace RobRichards\XMLSecLibs;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlseclibs.php
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2020, Robert Richards <rrichards@cdatazone.org>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Robert Richards nor the names of his
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @author Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @copyright 2007-2020 Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MoXMLSecurityKey
|
||||||
|
{
|
||||||
|
const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
|
||||||
|
const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
|
||||||
|
const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
|
||||||
|
const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
|
||||||
|
const AES128_GCM = 'http://www.w3.org/2009/xmlenc11#aes128-gcm';
|
||||||
|
const AES192_GCM = 'http://www.w3.org/2009/xmlenc11#aes192-gcm';
|
||||||
|
const AES256_GCM = 'http://www.w3.org/2009/xmlenc11#aes256-gcm';
|
||||||
|
const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
|
||||||
|
const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
|
||||||
|
const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
|
||||||
|
const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
|
||||||
|
const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
|
||||||
|
const RSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
|
||||||
|
const RSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
|
||||||
|
const HMAC_SHA1 = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
|
||||||
|
const AUTHTAG_LENGTH = 16;
|
||||||
|
|
||||||
|
/** @var array */
|
||||||
|
private $cryptParams = array();
|
||||||
|
|
||||||
|
/** @var int|string */
|
||||||
|
public $type = 0;
|
||||||
|
|
||||||
|
/** @var mixed|null */
|
||||||
|
public $key = null;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public $passphrase = "";
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $iv = null;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
public $name = null;
|
||||||
|
|
||||||
|
/** @var mixed|null */
|
||||||
|
public $keyChain = null;
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
public $isEncrypted = false;
|
||||||
|
|
||||||
|
/** @var MoXMLSecEnc|null */
|
||||||
|
public $encryptedCtx = null;
|
||||||
|
|
||||||
|
/** @var mixed|null */
|
||||||
|
public $guid = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This variable contains the certificate as a string if this key represents an X509-certificate.
|
||||||
|
* If this key doesn't represent a certificate, this will be null.
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $x509Certificate = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This variable contains the certificate thumbprint if we have loaded an X509-certificate.
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
private $X509Thumbprint = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $type
|
||||||
|
* @param null|array $params
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function __construct($type, $params=null)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case (self::TRIPLEDES_CBC):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'des-ede3-cbc';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
|
||||||
|
$this->cryptParams['keysize'] = 24;
|
||||||
|
$this->cryptParams['blocksize'] = 8;
|
||||||
|
break;
|
||||||
|
case (self::AES128_CBC):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-128-cbc';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
|
||||||
|
$this->cryptParams['keysize'] = 16;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::AES192_CBC):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-192-cbc';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
|
||||||
|
$this->cryptParams['keysize'] = 24;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::AES256_CBC):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-256-cbc';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
|
||||||
|
$this->cryptParams['keysize'] = 32;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::AES128_GCM):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-128-gcm';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes128-gcm';
|
||||||
|
$this->cryptParams['keysize'] = 32;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::AES192_GCM):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-192-gcm';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes192-gcm';
|
||||||
|
$this->cryptParams['keysize'] = 32;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::AES256_GCM):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['cipher'] = 'aes-256-gcm';
|
||||||
|
$this->cryptParams['type'] = 'symmetric';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2009/xmlenc11#aes256-gcm';
|
||||||
|
$this->cryptParams['keysize'] = 32;
|
||||||
|
$this->cryptParams['blocksize'] = 16;
|
||||||
|
break;
|
||||||
|
case (self::RSA_1_5):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::RSA_OAEP_MGF1P):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
|
||||||
|
$this->cryptParams['hash'] = null;
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::RSA_SHA1):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::RSA_SHA256):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
|
||||||
|
$this->cryptParams['digest'] = 'SHA256';
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::RSA_SHA384):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
|
||||||
|
$this->cryptParams['digest'] = 'SHA384';
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::RSA_SHA512):
|
||||||
|
$this->cryptParams['library'] = 'openssl';
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512';
|
||||||
|
$this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
|
||||||
|
$this->cryptParams['digest'] = 'SHA512';
|
||||||
|
if (is_array($params) && ! empty($params['type'])) {
|
||||||
|
if ($params['type'] == 'public' || $params['type'] == 'private') {
|
||||||
|
$this->cryptParams['type'] = $params['type'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Exception('Certificate "type" (private/public) must be passed via parameters');
|
||||||
|
case (self::HMAC_SHA1):
|
||||||
|
$this->cryptParams['library'] = $type;
|
||||||
|
$this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#hmac-sha1';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception('Invalid Key Type');
|
||||||
|
}
|
||||||
|
$this->type = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the key size for the symmetric encryption algorithm..
|
||||||
|
*
|
||||||
|
* If the key size is unknown, or this isn't a symmetric encryption algorithm,
|
||||||
|
* null is returned.
|
||||||
|
*
|
||||||
|
* @return int|null The number of bytes in the key.
|
||||||
|
*/
|
||||||
|
public function getSymmetricKeySize()
|
||||||
|
{
|
||||||
|
if (! isset($this->cryptParams['keysize'])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $this->cryptParams['keysize'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a session key using the openssl-extension.
|
||||||
|
* In case of using DES3-CBC the key is checked for a proper parity bits set.
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function generateSessionKey()
|
||||||
|
{
|
||||||
|
if (!isset($this->cryptParams['keysize'])) {
|
||||||
|
throw new Exception('Unknown key size for type "' . $this->type . '".');
|
||||||
|
}
|
||||||
|
$keysize = $this->cryptParams['keysize'];
|
||||||
|
|
||||||
|
$key = openssl_random_pseudo_bytes($keysize);
|
||||||
|
|
||||||
|
if ($this->type === self::TRIPLEDES_CBC) {
|
||||||
|
/* Make sure that the generated key has the proper parity bits set.
|
||||||
|
* Mcrypt doesn't care about the parity bits, but others may care.
|
||||||
|
*/
|
||||||
|
for ($i = 0; $i < strlen($key); $i++) {
|
||||||
|
$byte = ord($key[$i]) & 0xfe;
|
||||||
|
$parity = 1;
|
||||||
|
for ($j = 1; $j < 8; $j++) {
|
||||||
|
$parity ^= ($byte >> $j) & 1;
|
||||||
|
}
|
||||||
|
$byte |= $parity;
|
||||||
|
$key[$i] = chr($byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->key = $key;
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the raw thumbprint of a certificate
|
||||||
|
*
|
||||||
|
* @param string $cert
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public static function getRawThumbprint($cert)
|
||||||
|
{
|
||||||
|
|
||||||
|
$arCert = explode("\n", $cert);
|
||||||
|
$data = '';
|
||||||
|
$inData = false;
|
||||||
|
|
||||||
|
foreach ($arCert AS $curData) {
|
||||||
|
if (! $inData) {
|
||||||
|
if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
|
||||||
|
$inData = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$data .= trim($curData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! empty($data)) {
|
||||||
|
return strtolower(sha1(base64_decode($data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the given key, or - with isFile set true - the key from the keyfile.
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @param bool $isFile
|
||||||
|
* @param bool $isCert
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function loadKey($key, $isFile=false, $isCert = false)
|
||||||
|
{
|
||||||
|
if ($isFile) {
|
||||||
|
$this->key = file_get_contents($key);
|
||||||
|
} else {
|
||||||
|
$this->key = $key;
|
||||||
|
}
|
||||||
|
if ($isCert) {
|
||||||
|
$this->key = openssl_x509_read($this->key);
|
||||||
|
openssl_x509_export($this->key, $str_cert);
|
||||||
|
$this->x509Certificate = $str_cert;
|
||||||
|
$this->key = $str_cert;
|
||||||
|
} else {
|
||||||
|
$this->x509Certificate = null;
|
||||||
|
}
|
||||||
|
if ($this->cryptParams['library'] == 'openssl') {
|
||||||
|
switch ($this->cryptParams['type']) {
|
||||||
|
case 'public':
|
||||||
|
if ($isCert) {
|
||||||
|
/* Load the thumbprint if this is an X509 certificate. */
|
||||||
|
$this->X509Thumbprint = self::getRawThumbprint($this->key);
|
||||||
|
}
|
||||||
|
$this->key = openssl_get_publickey($this->key);
|
||||||
|
if (! $this->key) {
|
||||||
|
throw new Exception('Unable to extract public key');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'private':
|
||||||
|
$this->key = openssl_get_privatekey($this->key, $this->passphrase);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case'symmetric':
|
||||||
|
if (strlen($this->key) < $this->cryptParams['keysize']) {
|
||||||
|
throw new Exception('Key must contain at least 25 characters for this cipher');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Exception('Unknown type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO 10126 Padding
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @param integer $blockSize
|
||||||
|
* @throws Exception
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function padISO10126($data, $blockSize)
|
||||||
|
{
|
||||||
|
if ($blockSize > 256) {
|
||||||
|
throw new Exception('Block size higher than 256 not allowed');
|
||||||
|
}
|
||||||
|
$padChr = $blockSize - (strlen($data) % $blockSize);
|
||||||
|
$pattern = chr($padChr);
|
||||||
|
return $data . str_repeat($pattern, $padChr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove ISO 10126 Padding
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function unpadISO10126($data)
|
||||||
|
{
|
||||||
|
$padChr = substr($data, -1);
|
||||||
|
$padLen = ord($padChr);
|
||||||
|
return substr($data, 0, -$padLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function encryptSymmetric($data)
|
||||||
|
{
|
||||||
|
$this->iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->cryptParams['cipher']));
|
||||||
|
$authTag = null;
|
||||||
|
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) {
|
||||||
|
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
|
||||||
|
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms');
|
||||||
|
}
|
||||||
|
$authTag = openssl_random_pseudo_bytes(self::AUTHTAG_LENGTH);
|
||||||
|
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag);
|
||||||
|
} else {
|
||||||
|
$data = $this->padISO10126($data, $this->cryptParams['blocksize']);
|
||||||
|
$encrypted = openssl_encrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $encrypted) {
|
||||||
|
throw new Exception('Failure encrypting Data (openssl symmetric) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return $this->iv . $encrypted . $authTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function decryptSymmetric($data)
|
||||||
|
{
|
||||||
|
$iv_length = openssl_cipher_iv_length($this->cryptParams['cipher']);
|
||||||
|
$this->iv = substr($data, 0, $iv_length);
|
||||||
|
$data = substr($data, $iv_length);
|
||||||
|
$authTag = null;
|
||||||
|
if(in_array($this->cryptParams['cipher'], ['aes-128-gcm', 'aes-192-gcm', 'aes-256-gcm'])) {
|
||||||
|
if (version_compare(PHP_VERSION, '7.1.0') < 0) {
|
||||||
|
throw new Exception('PHP 7.1.0 is required to use AES GCM algorithms');
|
||||||
|
}
|
||||||
|
// obtain and remove the authentication tag
|
||||||
|
$offset = 0 - self::AUTHTAG_LENGTH;
|
||||||
|
$authTag = substr($data, $offset);
|
||||||
|
$data = substr($data, 0, $offset);
|
||||||
|
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA, $this->iv, $authTag);
|
||||||
|
} else {
|
||||||
|
$decrypted = openssl_decrypt($data, $this->cryptParams['cipher'], $this->key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $this->iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $decrypted) {
|
||||||
|
throw new Exception('Failure decrypting Data (openssl symmetric) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return null !== $authTag ? $decrypted : $this->unpadISO10126($decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given public data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function encryptPublic($data)
|
||||||
|
{
|
||||||
|
if (! openssl_public_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) {
|
||||||
|
throw new Exception('Failure encrypting Data (openssl public) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return $encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given public data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function decryptPublic($data)
|
||||||
|
{
|
||||||
|
if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
|
||||||
|
throw new Exception('Failure decrypting Data (openssl public) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return $decrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given private data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function encryptPrivate($data)
|
||||||
|
{
|
||||||
|
if (! openssl_private_encrypt($data, $encrypted, $this->key, $this->cryptParams['padding'])) {
|
||||||
|
throw new Exception('Failure encrypting Data (openssl private) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return $encrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given private data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function decryptPrivate($data)
|
||||||
|
{
|
||||||
|
if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
|
||||||
|
throw new Exception('Failure decrypting Data (openssl private) - ' . openssl_error_string());
|
||||||
|
}
|
||||||
|
return $decrypted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs the given data (string) using the openssl-extension
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return string
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function signOpenSSL($data)
|
||||||
|
{
|
||||||
|
$algo = OPENSSL_ALGO_SHA1;
|
||||||
|
if (! empty($this->cryptParams['digest'])) {
|
||||||
|
$algo = $this->cryptParams['digest'];
|
||||||
|
}
|
||||||
|
if (! openssl_sign($data, $signature, $this->key, $algo)) {
|
||||||
|
throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo);
|
||||||
|
}
|
||||||
|
return $signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the given data (string) belonging to the given signature using the openssl-extension
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 1 on succesful signature verification,
|
||||||
|
* 0 when signature verification failed,
|
||||||
|
* -1 if an error occurred during processing.
|
||||||
|
*
|
||||||
|
* NOTE: be very careful when checking the return value, because in PHP,
|
||||||
|
* -1 will be cast to True when in boolean context. So always check the
|
||||||
|
* return value in a strictly typed way, e.g. "$obj->verify(...) === 1".
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @param string $signature
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function verifyOpenSSL($data, $signature)
|
||||||
|
{
|
||||||
|
$algo = OPENSSL_ALGO_SHA1;
|
||||||
|
if (! empty($this->cryptParams['digest'])) {
|
||||||
|
$algo = $this->cryptParams['digest'];
|
||||||
|
}
|
||||||
|
return openssl_verify($data, $signature, $this->key, $algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function encryptData($data)
|
||||||
|
{
|
||||||
|
if ($this->cryptParams['library'] === 'openssl') {
|
||||||
|
switch ($this->cryptParams['type']) {
|
||||||
|
case 'symmetric':
|
||||||
|
return $this->encryptSymmetric($data);
|
||||||
|
case 'public':
|
||||||
|
return $this->encryptPublic($data);
|
||||||
|
case 'private':
|
||||||
|
return $this->encryptPrivate($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypts the given data (string) using the regarding php-extension, depending on the library assigned to algorithm in the contructor.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function decryptData($data)
|
||||||
|
{
|
||||||
|
if ($this->cryptParams['library'] === 'openssl') {
|
||||||
|
switch ($this->cryptParams['type']) {
|
||||||
|
case 'symmetric':
|
||||||
|
return $this->decryptSymmetric($data);
|
||||||
|
case 'public':
|
||||||
|
return $this->decryptPublic($data);
|
||||||
|
case 'private':
|
||||||
|
return $this->decryptPrivate($data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signs the data (string) using the extension assigned to the type in the constructor.
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @return mixed|string
|
||||||
|
*/
|
||||||
|
public function signData($data)
|
||||||
|
{
|
||||||
|
switch ($this->cryptParams['library']) {
|
||||||
|
case 'openssl':
|
||||||
|
return $this->signOpenSSL($data);
|
||||||
|
case (self::HMAC_SHA1):
|
||||||
|
return hash_hmac("sha1", $data, $this->key, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the data (string) against the given signature using the extension assigned to the type in the constructor.
|
||||||
|
*
|
||||||
|
* Returns in case of openSSL:
|
||||||
|
* 1 on succesful signature verification,
|
||||||
|
* 0 when signature verification failed,
|
||||||
|
* -1 if an error occurred during processing.
|
||||||
|
*
|
||||||
|
* NOTE: be very careful when checking the return value, because in PHP,
|
||||||
|
* -1 will be cast to True when in boolean context. So always check the
|
||||||
|
* return value in a strictly typed way, e.g. "$obj->verify(...) === 1".
|
||||||
|
*
|
||||||
|
* @param string $data
|
||||||
|
* @param string $signature
|
||||||
|
* @return bool|int
|
||||||
|
*/
|
||||||
|
public function verifySignature($data, $signature)
|
||||||
|
{
|
||||||
|
switch ($this->cryptParams['library']) {
|
||||||
|
case 'openssl':
|
||||||
|
return $this->verifyOpenSSL($data, $signature);
|
||||||
|
case (self::HMAC_SHA1):
|
||||||
|
$expectedSignature = hash_hmac("sha1", $data, $this->key, true);
|
||||||
|
return strcmp($signature, $expectedSignature) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
* @see getAlgorithm()
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAlgorith()
|
||||||
|
{
|
||||||
|
return $this->getAlgorithm();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getAlgorithm()
|
||||||
|
{
|
||||||
|
return $this->cryptParams['method'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param int $type
|
||||||
|
* @param string $string
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public static function makeAsnSegment($type, $string)
|
||||||
|
{
|
||||||
|
switch ($type) {
|
||||||
|
case 0x02:
|
||||||
|
if (ord($string) > 0x7f)
|
||||||
|
$string = chr(0).$string;
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
$string = chr(0).$string;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$length = strlen($string);
|
||||||
|
|
||||||
|
if ($length < 128) {
|
||||||
|
$output = sprintf("%c%c%s", $type, $length, $string);
|
||||||
|
} else if ($length < 0x0100) {
|
||||||
|
$output = sprintf("%c%c%c%s", $type, 0x81, $length, $string);
|
||||||
|
} else if ($length < 0x010000) {
|
||||||
|
$output = sprintf("%c%c%c%c%s", $type, 0x82, $length / 0x0100, $length % 0x0100, $string);
|
||||||
|
} else {
|
||||||
|
$output = null;
|
||||||
|
}
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Hint: Modulus and Exponent must already be base64 decoded
|
||||||
|
* @param string $modulus
|
||||||
|
* @param string $exponent
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertRSA($modulus, $exponent)
|
||||||
|
{
|
||||||
|
/* make an ASN publicKeyInfo */
|
||||||
|
$exponentEncoding = self::makeAsnSegment(0x02, $exponent);
|
||||||
|
$modulusEncoding = self::makeAsnSegment(0x02, $modulus);
|
||||||
|
$sequenceEncoding = self::makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
|
||||||
|
$bitstringEncoding = self::makeAsnSegment(0x03, $sequenceEncoding);
|
||||||
|
$rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
|
||||||
|
$publicKeyInfo = self::makeAsnSegment(0x30, $rsaAlgorithmIdentifier.$bitstringEncoding);
|
||||||
|
|
||||||
|
/* encode the publicKeyInfo in base64 and add PEM brackets */
|
||||||
|
$publicKeyInfoBase64 = base64_encode($publicKeyInfo);
|
||||||
|
$encoding = "-----BEGIN PUBLIC KEY-----\n";
|
||||||
|
$offset = 0;
|
||||||
|
while ($segment = substr($publicKeyInfoBase64, $offset, 64)) {
|
||||||
|
$encoding = $encoding.$segment."\n";
|
||||||
|
$offset += 64;
|
||||||
|
}
|
||||||
|
return $encoding."-----END PUBLIC KEY-----\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $parent
|
||||||
|
*/
|
||||||
|
public function serializeKey($parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the X509 certificate this key represents.
|
||||||
|
*
|
||||||
|
* Will return the X509 certificate in PEM-format if this key represents
|
||||||
|
* an X509 certificate.
|
||||||
|
*
|
||||||
|
* @return string The X509 certificate or null if this key doesn't represent an X509-certificate.
|
||||||
|
*/
|
||||||
|
public function getX509Certificate()
|
||||||
|
{
|
||||||
|
return $this->x509Certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the thumbprint of this X509 certificate.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* The thumbprint as a lowercase 40-character hexadecimal number, or null
|
||||||
|
* if this isn't a X509 certificate.
|
||||||
|
*
|
||||||
|
* @return string Lowercase 40-character hexadecimal number of thumbprint
|
||||||
|
*/
|
||||||
|
public function getX509Thumbprint()
|
||||||
|
{
|
||||||
|
return $this->X509Thumbprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create key from an EncryptedKey-element.
|
||||||
|
*
|
||||||
|
* @param DOMElement $element The EncryptedKey-element.
|
||||||
|
* @return MoXMLSecurityKey The new key.
|
||||||
|
* @throws Exception
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static function fromEncryptedKeyElement(DOMElement $element)
|
||||||
|
{
|
||||||
|
|
||||||
|
$objenc = new MoXMLSecEnc();
|
||||||
|
$objenc->setNode($element);
|
||||||
|
if (! $objKey = $objenc->locateKey()) {
|
||||||
|
throw new Exception("Unable to locate algorithm for this Encrypted Key");
|
||||||
|
}
|
||||||
|
$objKey->isEncrypted = true;
|
||||||
|
$objKey->encryptedCtx = $objenc;
|
||||||
|
MoXMLSecEnc::staticLocateKeyInfo($objKey, $element);
|
||||||
|
return $objKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
core/lib/saml2/SAML2Core/Utils/MoXPath.php
Normal file
44
core/lib/saml2/SAML2Core/Utils/MoXPath.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace RobRichards\XMLSecLibs\Utils;
|
||||||
|
|
||||||
|
class MoXPath
|
||||||
|
{
|
||||||
|
const ALPHANUMERIC = '\w\d';
|
||||||
|
const NUMERIC = '\d';
|
||||||
|
const LETTERS = '\w';
|
||||||
|
const EXTENDED_ALPHANUMERIC = '\w\d\s\-_:\.';
|
||||||
|
|
||||||
|
const SINGLE_QUOTE = '\'';
|
||||||
|
const DOUBLE_QUOTE = '"';
|
||||||
|
const ALL_QUOTES = '[\'"]';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter an attribute value for save inclusion in an XPath query.
|
||||||
|
*
|
||||||
|
* @param string $value The value to filter.
|
||||||
|
* @param string $quotes The quotes used to delimit the value in the XPath query.
|
||||||
|
*
|
||||||
|
* @return string The filtered attribute value.
|
||||||
|
*/
|
||||||
|
public static function filterAttrValue($value, $quotes = self::ALL_QUOTES)
|
||||||
|
{
|
||||||
|
return preg_replace('#'.$quotes.'#', '', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter an attribute name for save inclusion in an XPath query.
|
||||||
|
*
|
||||||
|
* @param string $name The attribute name to filter.
|
||||||
|
* @param mixed $allow The set of characters to allow. Can be one of the constants provided by this class, or a
|
||||||
|
* custom regex excluding the '#' character (used as delimiter).
|
||||||
|
*
|
||||||
|
* @return string The filtered attribute name.
|
||||||
|
*/
|
||||||
|
public static function filterAttrName($name, $allow = self::EXTENDED_ALPHANUMERIC)
|
||||||
|
{
|
||||||
|
return preg_replace('#[^'.$allow.']#', '', $name);
|
||||||
|
}
|
||||||
|
}
|
||||||
830
core/lib/saml2/Utilities.php
Normal file
830
core/lib/saml2/Utilities.php
Normal file
@@ -0,0 +1,830 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is part of miniOrange SAML plugin.
|
||||||
|
*
|
||||||
|
* miniOrange SAML plugin is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* miniOrange SAML plugin is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with miniOrange SAML plugin. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
include_once 'xmlseclibs.php';
|
||||||
|
use \RobRichards\XMLSecLibs\MoXMLSecurityKey;
|
||||||
|
use \RobRichards\XMLSecLibs\MoXMLSecurityDSig;
|
||||||
|
use \RobRichards\XMLSecLibs\MoXMLSecEnc;
|
||||||
|
|
||||||
|
class Utilities {
|
||||||
|
|
||||||
|
public static function generateID() {
|
||||||
|
return '_' . self::stringToHex(self::generateRandomBytes(21));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stringToHex($bytes) {
|
||||||
|
$ret = '';
|
||||||
|
for($i = 0; $i < strlen($bytes); $i++) {
|
||||||
|
$ret .= sprintf('%02x', ord($bytes[$i]));
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function generateRandomBytes($length, $fallback = TRUE) {
|
||||||
|
|
||||||
|
return openssl_random_pseudo_bytes($length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createAuthnRequest($acsUrl, $issuer, $force_authn = 'false') {
|
||||||
|
$requestXmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
|
||||||
|
'<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="' . self::generateID() .
|
||||||
|
'" Version="2.0" IssueInstant="' . self::generateTimestamp() . '"';
|
||||||
|
if( $force_authn == 'true') {
|
||||||
|
$requestXmlStr .= ' ForceAuthn="true"';
|
||||||
|
}
|
||||||
|
$requestXmlStr .= ' ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="' . $acsUrl .
|
||||||
|
'" ><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . $issuer . '</saml:Issuer></samlp:AuthnRequest>';
|
||||||
|
$deflatedStr = gzdeflate($requestXmlStr);
|
||||||
|
$base64EncodedStr = base64_encode($deflatedStr);
|
||||||
|
$urlEncoded = urlencode($base64EncodedStr);
|
||||||
|
update_option('MO_SAML_REQUEST',$base64EncodedStr);
|
||||||
|
|
||||||
|
return $urlEncoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function createSAMLRequest($acsUrl, $issuer, $destination, $force_authn = 'false') {
|
||||||
|
|
||||||
|
$requestXmlStr = '<?xml version="1.0" encoding="UTF-8"?>' .
|
||||||
|
'<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="' . self::generateID() .
|
||||||
|
'" Version="2.0" IssueInstant="' . self::generateTimestamp() . '"';
|
||||||
|
if( $force_authn == 'true') {
|
||||||
|
$requestXmlStr .= ' ForceAuthn="true"';
|
||||||
|
}
|
||||||
|
$requestXmlStr .= ' ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="' . $acsUrl .
|
||||||
|
'" Destination="' . htmlspecialchars($destination) . '"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' . $issuer . '</saml:Issuer><samlp:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||||
|
/></samlp:AuthnRequest>';
|
||||||
|
$samlRequest = base64_encode($requestXmlStr);
|
||||||
|
update_option('MO_SAML_REQUEST',$samlRequest);
|
||||||
|
return $requestXmlStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function generateTimestamp($instant = NULL) {
|
||||||
|
if($instant === NULL) {
|
||||||
|
$instant = time();
|
||||||
|
}
|
||||||
|
return gmdate('Y-m-d\TH:i:s\Z', $instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function xpQuery(DOMNode $node, $query)
|
||||||
|
{
|
||||||
|
|
||||||
|
static $xpCache = NULL;
|
||||||
|
|
||||||
|
if ($node instanceof DOMDocument) {
|
||||||
|
$doc = $node;
|
||||||
|
} else {
|
||||||
|
$doc = $node->ownerDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($xpCache === NULL || !$xpCache->document->isSameNode($doc)) {
|
||||||
|
$xpCache = new DOMXPath($doc);
|
||||||
|
$xpCache->registerNamespace('soap-env', 'http://schemas.xmlsoap.org/soap/envelope/');
|
||||||
|
$xpCache->registerNamespace('saml_protocol', 'urn:oasis:names:tc:SAML:2.0:protocol');
|
||||||
|
$xpCache->registerNamespace('saml_assertion', 'urn:oasis:names:tc:SAML:2.0:assertion');
|
||||||
|
$xpCache->registerNamespace('saml_metadata', 'urn:oasis:names:tc:SAML:2.0:metadata');
|
||||||
|
$xpCache->registerNamespace('ds', 'http://www.w3.org/2000/09/xmldsig#');
|
||||||
|
$xpCache->registerNamespace('xenc', 'http://www.w3.org/2001/04/xmlenc#');
|
||||||
|
}
|
||||||
|
|
||||||
|
$results = $xpCache->query($query, $node);
|
||||||
|
$ret = array();
|
||||||
|
for ($i = 0; $i < $results->length; $i++) {
|
||||||
|
$ret[$i] = $results->item($i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function parseNameId(DOMElement $xml)
|
||||||
|
{
|
||||||
|
$ret = array('Value' => trim($xml->textContent));
|
||||||
|
|
||||||
|
foreach (array('NameQualifier', 'SPNameQualifier', 'Format') as $attr) {
|
||||||
|
if ($xml->hasAttribute($attr)) {
|
||||||
|
$ret[$attr] = $xml->getAttribute($attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function xsDateTimeToTimestamp($time)
|
||||||
|
{
|
||||||
|
$matches = array();
|
||||||
|
|
||||||
|
// We use a very strict regex to parse the timestamp.
|
||||||
|
$regex = '/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d+)?Z$/D';
|
||||||
|
if (preg_match($regex, $time, $matches) == 0) {
|
||||||
|
echo sprintf("Invalid SAML2 timestamp passed to xsDateTimeToTimestamp: ". htmlspecialchars($time));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the different components of the time from the matches in the regex.
|
||||||
|
// intval will ignore leading zeroes in the string.
|
||||||
|
$year = intval($matches[1]);
|
||||||
|
$month = intval($matches[2]);
|
||||||
|
$day = intval($matches[3]);
|
||||||
|
$hour = intval($matches[4]);
|
||||||
|
$minute = intval($matches[5]);
|
||||||
|
$second = intval($matches[6]);
|
||||||
|
|
||||||
|
// We use gmmktime because the timestamp will always be given
|
||||||
|
//in UTC.
|
||||||
|
$ts = gmmktime($hour, $minute, $second, $month, $day, $year);
|
||||||
|
|
||||||
|
return $ts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function extractStrings(DOMElement $parent, $namespaceURI, $localName)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$ret = array();
|
||||||
|
for ($node = $parent->firstChild; $node !== NULL; $node = $node->nextSibling) {
|
||||||
|
if ($node->namespaceURI !== $namespaceURI || $node->localName !== $localName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ret[] = trim($node->textContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function validateElement(DOMElement $root)
|
||||||
|
{
|
||||||
|
//$data = $root->ownerDocument->saveXML($root);
|
||||||
|
//echo htmlspecialchars($data);
|
||||||
|
|
||||||
|
/* Create an XML security object. */
|
||||||
|
$objXMLSecDSig = new MoXMLSecurityDSig();
|
||||||
|
|
||||||
|
/* Both SAML messages and SAML assertions use the 'ID' attribute. */
|
||||||
|
$objXMLSecDSig->idKeys[] = 'ID';
|
||||||
|
|
||||||
|
|
||||||
|
/* Locate the XMLDSig Signature element to be used. */
|
||||||
|
$signatureElement = self::xpQuery($root, './ds:Signature');
|
||||||
|
//print_r($signatureElement);
|
||||||
|
|
||||||
|
if (count($signatureElement) === 0) {
|
||||||
|
/* We don't have a signature element to validate. */
|
||||||
|
return FALSE;
|
||||||
|
} elseif (count($signatureElement) > 1) {
|
||||||
|
echo sprintf("XMLSec: more than one signature element in root.");
|
||||||
|
exit;
|
||||||
|
}/* elseif ((in_array('Response', $signatureElement) && $ocurrence['Response'] > 1) ||
|
||||||
|
(in_array('Assertion', $signatureElement) && $ocurrence['Assertion'] > 1) ||
|
||||||
|
!in_array('Response', $signatureElement) && !in_array('Assertion', $signatureElement)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} */
|
||||||
|
|
||||||
|
$signatureElement = $signatureElement[0];
|
||||||
|
$objXMLSecDSig->sigNode = $signatureElement;
|
||||||
|
|
||||||
|
/* Canonicalize the XMLDSig SignedInfo element in the message. */
|
||||||
|
$objXMLSecDSig->canonicalizeSignedInfo();
|
||||||
|
|
||||||
|
/* Validate referenced xml nodes. */
|
||||||
|
if (!$objXMLSecDSig->validateReference()) {
|
||||||
|
echo sprintf("XMLSec: digest validation failed");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that $root is one of the signed nodes. */
|
||||||
|
$rootSigned = FALSE;
|
||||||
|
/** @var DOMNode $signedNode */
|
||||||
|
foreach ($objXMLSecDSig->getValidatedNodes() as $signedNode) {
|
||||||
|
if ($signedNode->isSameNode($root)) {
|
||||||
|
$rootSigned = TRUE;
|
||||||
|
break;
|
||||||
|
} elseif ($root->parentNode instanceof DOMDocument && $signedNode->isSameNode($root->ownerDocument)) {
|
||||||
|
/* $root is the root element of a signed document. */
|
||||||
|
$rootSigned = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$rootSigned) {
|
||||||
|
echo sprintf("XMLSec: The root element is not signed.");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we extract all available X509 certificates in the signature element. */
|
||||||
|
$certificates = array();
|
||||||
|
foreach (self::xpQuery($signatureElement, './ds:KeyInfo/ds:X509Data/ds:X509Certificate') as $certNode) {
|
||||||
|
$certData = trim($certNode->textContent);
|
||||||
|
$certData = str_replace(array("\r", "\n", "\t", ' '), '', $certData);
|
||||||
|
$certificates[] = $certData;
|
||||||
|
//echo "CertDate: " . $certData . "<br />";
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = array(
|
||||||
|
'Signature' => $objXMLSecDSig,
|
||||||
|
'Certificates' => $certificates,
|
||||||
|
);
|
||||||
|
|
||||||
|
//echo "Signature validated";
|
||||||
|
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static function validateSignature(array $info, MoXMLSecurityKey $key)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/** @var MoXMLSecurityDSig $objXMLSecDSig */
|
||||||
|
$objXMLSecDSig = $info['Signature'];
|
||||||
|
|
||||||
|
$sigMethod = self::xpQuery($objXMLSecDSig->sigNode, './ds:SignedInfo/ds:SignatureMethod');
|
||||||
|
if (empty($sigMethod)) {
|
||||||
|
echo sprintf('Missing SignatureMethod element');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$sigMethod = $sigMethod[0];
|
||||||
|
if (!$sigMethod->hasAttribute('Algorithm')) {
|
||||||
|
echo sprintf('Missing Algorithm-attribute on SignatureMethod element.');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$algo = $sigMethod->getAttribute('Algorithm');
|
||||||
|
|
||||||
|
if ($key->type === MoXMLSecurityKey::RSA_SHA1 && $algo !== $key->type) {
|
||||||
|
$key = self::castKey($key, $algo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the signature. */
|
||||||
|
if (! $objXMLSecDSig->verify($key)) {
|
||||||
|
echo sprintf('Unable to validate Signature');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function castKey(MoXMLSecurityKey $key, $algorithm, $type = 'public')
|
||||||
|
{
|
||||||
|
|
||||||
|
// do nothing if algorithm is already the type of the key
|
||||||
|
if ($key->type === $algorithm) {
|
||||||
|
return $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$keyInfo = openssl_pkey_get_details($key->key);
|
||||||
|
if ($keyInfo === FALSE) {
|
||||||
|
echo sprintf('Unable to get key details from XMLSecurityKey.');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if (!isset($keyInfo['key'])) {
|
||||||
|
echo sprintf('Missing key in public key details.');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$newKey = new MoXMLSecurityKey($algorithm, array('type'=>$type));
|
||||||
|
$newKey->loadKey($keyInfo['key']);
|
||||||
|
|
||||||
|
return $newKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function processResponse($currentURL, $certFingerprint, $signatureData,
|
||||||
|
SAML2_Response $response, $certNumber,$relayState) {
|
||||||
|
|
||||||
|
$assertion = current($response->getAssertions());
|
||||||
|
|
||||||
|
$notBefore = $assertion->getNotBefore();
|
||||||
|
if ($notBefore !== NULL && $notBefore > time() + 60) {
|
||||||
|
error_log('Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notOnOrAfter = $assertion->getNotOnOrAfter();
|
||||||
|
if ($notOnOrAfter !== NULL && $notOnOrAfter <= time() - 60) {
|
||||||
|
error_log('Received an assertion that has expired. Check clock synchronization on IdP and SP.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sessionNotOnOrAfter = $assertion->getSessionNotOnOrAfter();
|
||||||
|
if ($sessionNotOnOrAfter !== NULL && $sessionNotOnOrAfter <= time() - 60) {
|
||||||
|
error_log('Received an assertion with a session that has expired. Check clock synchronization on IdP and SP.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate Response-element destination. */
|
||||||
|
$msgDestination = $response->getDestination();
|
||||||
|
if(substr($msgDestination, -1) == '/') {
|
||||||
|
$msgDestination = substr($msgDestination, 0, -1);
|
||||||
|
}
|
||||||
|
if(substr($currentURL, -1) == '/') {
|
||||||
|
$currentURL = substr($currentURL, 0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($msgDestination !== NULL && $msgDestination !== $currentURL) {
|
||||||
|
echo sprintf('Destination in response doesn\'t match the current URL. Destination is "' .
|
||||||
|
htmlspecialchars($msgDestination) . '", current URL is "' . htmlspecialchars($currentURL) . '".');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$responseSigned = self::checkSign($certFingerprint, $signatureData, $certNumber,$relayState);
|
||||||
|
|
||||||
|
if (!$responseSigned) {
|
||||||
|
error_log('SAML: Responses is not signed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returning boolean $responseSigned */
|
||||||
|
return $responseSigned;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function checkSign($certFingerprint, $signatureData, $certNumber, $relayState) {
|
||||||
|
$certificates = $signatureData['Certificates'];
|
||||||
|
error_log('SAML: certificate count = '.count($certificates));
|
||||||
|
if (count($certificates) === 0) {
|
||||||
|
$storedCerts = maybe_unserialize(get_option('saml_x509_certificate'));
|
||||||
|
$pemCert = $storedCerts[$certNumber];
|
||||||
|
}else{
|
||||||
|
$fpArray = array();
|
||||||
|
$fpArray[] = $certFingerprint;
|
||||||
|
$pemCert = self::findCertificate($fpArray, $certificates, $relayState);
|
||||||
|
if($pemCert==false)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lastException = NULL;
|
||||||
|
|
||||||
|
$key = new MoXMLSecurityKey(MoXMLSecurityKey::RSA_SHA1, array('type'=>'public'));
|
||||||
|
$key->loadKey($pemCert);
|
||||||
|
|
||||||
|
try {
|
||||||
|
/*
|
||||||
|
* Make sure that we have a valid signature
|
||||||
|
*/
|
||||||
|
self::validateSignature($signatureData, $key);
|
||||||
|
return TRUE;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$lastException = $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* We were unable to validate the signature with any of our keys. */
|
||||||
|
if ($lastException !== NULL) {
|
||||||
|
throw $lastException;
|
||||||
|
} else {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function validateIssuerAndAudience($samlResponse, $spEntityId, $issuerToValidateAgainst, $relayState) {
|
||||||
|
$issuer = current($samlResponse->getAssertions())->getIssuer();
|
||||||
|
$assertion = current($samlResponse->getAssertions());
|
||||||
|
$audiences = $assertion->getValidAudiences();
|
||||||
|
if(strcmp($issuerToValidateAgainst, $issuer) === 0) {
|
||||||
|
if(!empty($audiences)) {
|
||||||
|
if(in_array($spEntityId, $audiences, TRUE)) {
|
||||||
|
return TRUE;
|
||||||
|
} else {
|
||||||
|
if($relayState=='testValidate'){
|
||||||
|
$Error_message=mo_saml_options_error_constants::Error_invalid_audience;
|
||||||
|
$Cause_message = mo_saml_options_error_constants::Cause_invalid_audience;
|
||||||
|
echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||||
|
echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;"> ' . __('ERROR','miniorange-saml-20-single-sign-on') . '</div>
|
||||||
|
<div style="color: #a94442;font-size:14pt; margin-bottom:20px;"><p><strong>' . __('Error','miniorange-saml-20-single-sign-on') . ': </strong>'.$Error_message.'</p>
|
||||||
|
|
||||||
|
<p><strong>' . __('Possible Cause','miniorange-saml-20-single-sign-on'). ': </strong>'.$Cause_message.'</p>
|
||||||
|
<p>' . __('Expected one of the Audiences to be','miniorange-saml-20-single-sign-on'). ': '.$spEntityId.'<p>
|
||||||
|
</div>';
|
||||||
|
mo_saml_download_logs($Error_message,$Cause_message);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wp_die(__("We could not sign you in. Please contact your administrator",'miniorange-saml-20-single-sign-on'),"Error: Invalid Audience URI");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if($relayState=='testValidate'){
|
||||||
|
|
||||||
|
$Error_message=mo_saml_options_error_constants::Error_issuer_not_verfied;
|
||||||
|
$Cause_message = mo_saml_options_error_constants::Cause_issuer_not_verfied;
|
||||||
|
update_option('mo_saml_required_issuer',$issuer);
|
||||||
|
echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||||
|
echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;">' . __('ERROR','miniorange-saml-20-single-sign-on') . '</div>
|
||||||
|
<div style="color: #a94442;font-size:14pt; margin-bottom:20px;text-align: justify"><p><strong>' . __('Error','miniorange-saml-20-single-sign-on'). ':'.$Error_message.' </strong></p>
|
||||||
|
|
||||||
|
<p><strong>' . __('Possible Cause','miniorange-saml-20-single-sign-on') . ':'.$Cause_message.' </strong></p>
|
||||||
|
<div>
|
||||||
|
<ol style="text-align: center">
|
||||||
|
<form method="post" action="" name="mo_fix_entityid" id="mo_fix_certificate">';
|
||||||
|
wp_nonce_field('mo_fix_entity_id');
|
||||||
|
echo '<input type="hidden" name="option" value="mo_fix_entity_id" />
|
||||||
|
<input type="submit" class="miniorange-button" style="width: 55%" value="' . __('Fix Issue','miniorange-saml-20-single-sign-on' ) .'">
|
||||||
|
</form>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>';
|
||||||
|
|
||||||
|
mo_saml_download_logs($Error_message,$Cause_message);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wp_die(__("We could not sign you in. Please contact your administrator",'miniorange-saml-20-single-sign-on'),"Error: Issuer cannot be verified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function findCertificate(array $certFingerprints, array $certificates, $relayState) {
|
||||||
|
|
||||||
|
$candidates = array();
|
||||||
|
|
||||||
|
//foreach ($certificates as $cert) {
|
||||||
|
$fp = strtolower(sha1(base64_decode($certificates[0])));
|
||||||
|
if (!in_array($fp, $certFingerprints, TRUE)) {
|
||||||
|
$candidates[] = $fp;
|
||||||
|
return false;
|
||||||
|
//continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have found a matching fingerprint. */
|
||||||
|
$pem = "-----BEGIN CERTIFICATE-----\n" .
|
||||||
|
chunk_split($certificates[0], 64) .
|
||||||
|
"-----END CERTIFICATE-----\n";
|
||||||
|
|
||||||
|
return $pem;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if($relayState=='testValidate'){
|
||||||
|
// $pem = "-----BEGIN CERTIFICATE-----<br>" .
|
||||||
|
// chunk_split($cert, 64) .
|
||||||
|
// "<br>-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
// echo '<div style="font-family:Calibri;padding:0 3%;">';
|
||||||
|
// echo '<div style="color: #a94442;background-color: #f2dede;padding: 15px;margin-bottom: 20px;text-align:center;border:1px solid #E6B3B2;font-size:18pt;"> ERROR</div>
|
||||||
|
// <div style="color: #a94442;font-size:14pt; margin-bottom:20px;"><p><strong>Error: </strong>Unable to find a certificate matching the configured fingerprint.</p>
|
||||||
|
// <p>Please contact your administrator and report the following error:</p>
|
||||||
|
// <p><strong>Possible Cause: </strong>Content of \'X.509 Certificate\' field in Service Provider Settings is incorrect. Please replace it with certificate given below.</p>
|
||||||
|
// <p><strong>Certificate found in SAML Response: </strong><br><br>'.$pem.'</p>
|
||||||
|
// </div>
|
||||||
|
// <div style="margin:3%;display:block;text-align:center;">
|
||||||
|
// <form action="index.php">
|
||||||
|
// <div style="margin:3%;display:block;text-align:center;"><input style="padding:1%;width:100px;background: #0091CD none repeat scroll 0% 0%;cursor: pointer;font-size:15px;border-width: 1px;border-style: solid;border-radius: 3px;white-space: nowrap;box-sizing: border-box;border-color: #0073AA;box-shadow: 0px 1px 0px rgba(120, 200, 230, 0.6) inset;color: #FFF;"type="button" value="Done" onClick="self.close();"></div>';
|
||||||
|
|
||||||
|
// exit;
|
||||||
|
// }
|
||||||
|
// else{
|
||||||
|
// wp_die("We could not sign you in. Please contact your administrator","Error: Invalid Certificate");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt an encrypted element.
|
||||||
|
*
|
||||||
|
* This is an internal helper function.
|
||||||
|
*
|
||||||
|
* @param DOMElement $encryptedData The encrypted data.
|
||||||
|
* @param MoXMLSecurityKey $inputKey The decryption key.
|
||||||
|
* @param array &$blacklist Blacklisted decryption algorithms.
|
||||||
|
* @return DOMElement The decrypted element.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private static function doDecryptElement(DOMElement $encryptedData, MoXMLSecurityKey $inputKey, array &$blacklist)
|
||||||
|
{
|
||||||
|
$enc = new MoXMLSecEnc();
|
||||||
|
$enc->setNode($encryptedData);
|
||||||
|
|
||||||
|
$enc->type = $encryptedData->getAttribute("Type");
|
||||||
|
$symmetricKey = $enc->locateKey($encryptedData);
|
||||||
|
if (!$symmetricKey) {
|
||||||
|
echo sprintf(__('Could not locate key algorithm in encrypted data.','miniorange-saml-20-single-sign-on'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey);
|
||||||
|
if (!$symmetricKeyInfo) {
|
||||||
|
echo sprintf(__('Could not locate <dsig:KeyInfo> for the encrypted key.','miniorange-saml-20-single-sign-on'));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$inputKeyAlgo = $inputKey->getAlgorith();
|
||||||
|
if ($symmetricKeyInfo->isEncrypted) {
|
||||||
|
$symKeyInfoAlgo = $symmetricKeyInfo->getAlgorith();
|
||||||
|
if (in_array($symKeyInfoAlgo, $blacklist, TRUE)) {
|
||||||
|
echo sprintf('Algorithm disabled: ' . var_export($symKeyInfoAlgo, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($symKeyInfoAlgo === MoXMLSecurityKey::RSA_OAEP_MGF1P && $inputKeyAlgo === MoXMLSecurityKey::RSA_1_5) {
|
||||||
|
/*
|
||||||
|
* The RSA key formats are equal, so loading an RSA_1_5 key
|
||||||
|
* into an RSA_OAEP_MGF1P key can be done without problems.
|
||||||
|
* We therefore pretend that the input key is an
|
||||||
|
* RSA_OAEP_MGF1P key.
|
||||||
|
*/
|
||||||
|
$inputKeyAlgo = MoXMLSecurityKey::RSA_OAEP_MGF1P;
|
||||||
|
}
|
||||||
|
/* Make sure that the input key format is the same as the one used to encrypt the key. */
|
||||||
|
if ($inputKeyAlgo !== $symKeyInfoAlgo) {
|
||||||
|
echo sprintf( 'Algorithm mismatch between input key and key used to encrypt ' .
|
||||||
|
' the symmetric key for the message. Key was: ' .
|
||||||
|
var_export($inputKeyAlgo, TRUE) . '; message was: ' .
|
||||||
|
var_export($symKeyInfoAlgo, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
/** @var MoXMLSecEnc $encKey */
|
||||||
|
$encKey = $symmetricKeyInfo->encryptedCtx;
|
||||||
|
$symmetricKeyInfo->key = $inputKey->key;
|
||||||
|
$keySize = $symmetricKey->getSymmetricKeySize();
|
||||||
|
if ($keySize === NULL) {
|
||||||
|
/* To protect against "key oracle" attacks, we need to be able to create a
|
||||||
|
* symmetric key, and for that we need to know the key size.
|
||||||
|
*/
|
||||||
|
echo sprintf('Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$key = $encKey->decryptKey($symmetricKeyInfo);
|
||||||
|
if (strlen($key) != $keySize) {
|
||||||
|
echo sprintf('Unexpected key size (' . strlen($key) * 8 . 'bits) for encryption algorithm: ' .
|
||||||
|
var_export($symmetricKey->type, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
/* We failed to decrypt this key. Log it, and substitute a "random" key. */
|
||||||
|
|
||||||
|
/* Create a replacement key, so that it looks like we fail in the same way as if the key was correctly padded. */
|
||||||
|
/* We base the symmetric key on the encrypted key and private key, so that we always behave the
|
||||||
|
* same way for a given input key.
|
||||||
|
*/
|
||||||
|
$encryptedKey = $encKey->getCipherValue();
|
||||||
|
$pkey = openssl_pkey_get_details($symmetricKeyInfo->key);
|
||||||
|
$pkey = sha1(serialize($pkey), TRUE);
|
||||||
|
$key = sha1($encryptedKey . $pkey, TRUE);
|
||||||
|
/* Make sure that the key has the correct length. */
|
||||||
|
if (strlen($key) > $keySize) {
|
||||||
|
$key = substr($key, 0, $keySize);
|
||||||
|
} elseif (strlen($key) < $keySize) {
|
||||||
|
$key = str_pad($key, $keySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$symmetricKey->loadkey($key);
|
||||||
|
} else {
|
||||||
|
$symKeyAlgo = $symmetricKey->getAlgorith();
|
||||||
|
/* Make sure that the input key has the correct format. */
|
||||||
|
if ($inputKeyAlgo !== $symKeyAlgo) {
|
||||||
|
echo sprintf( 'Algorithm mismatch between input key and key in message. ' .
|
||||||
|
'Key was: ' . var_export($inputKeyAlgo, TRUE) . '; message was: ' .
|
||||||
|
var_export($symKeyAlgo, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
$symmetricKey = $inputKey;
|
||||||
|
}
|
||||||
|
$algorithm = $symmetricKey->getAlgorith();
|
||||||
|
if (in_array($algorithm, $blacklist, TRUE)) {
|
||||||
|
echo sprintf('Algorithm disabled: ' . var_export($algorithm, TRUE));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
/** @var string $decrypted */
|
||||||
|
$decrypted = $enc->decryptNode($symmetricKey, FALSE);
|
||||||
|
/*
|
||||||
|
* This is a workaround for the case where only a subset of the XML
|
||||||
|
* tree was serialized for encryption. In that case, we may miss the
|
||||||
|
* namespaces needed to parse the XML.
|
||||||
|
*/
|
||||||
|
$xml = '<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '.
|
||||||
|
'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' .
|
||||||
|
$decrypted .
|
||||||
|
'</root>';
|
||||||
|
$newDoc = new DOMDocument();
|
||||||
|
if (!@$newDoc->loadXML($xml)) {
|
||||||
|
echo sprintf('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?');
|
||||||
|
throw new Exception('Failed to parse decrypted XML. Maybe the wrong sharedkey was used?');
|
||||||
|
}
|
||||||
|
$decryptedElement = $newDoc->firstChild->firstChild;
|
||||||
|
if ($decryptedElement === NULL) {
|
||||||
|
echo sprintf('Missing encrypted element.');
|
||||||
|
throw new Exception('Missing encrypted element.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!($decryptedElement instanceof DOMElement)) {
|
||||||
|
echo sprintf('Decrypted element was not actually a DOMElement.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $decryptedElement;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Decrypt an encrypted element.
|
||||||
|
*
|
||||||
|
* @param DOMElement $encryptedData The encrypted data.
|
||||||
|
* @param MoXMLSecurityKey $inputKey The decryption key.
|
||||||
|
* @param array $blacklist Blacklisted decryption algorithms.
|
||||||
|
* @return DOMElement The decrypted element.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static function decryptElement(DOMElement $encryptedData, MoXMLSecurityKey $inputKey, array $blacklist = array(), MoXMLSecurityKey $alternateKey = NULL)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return self::doDecryptElement($encryptedData, $inputKey, $blacklist);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
//Try with alternate key
|
||||||
|
try {
|
||||||
|
return self::doDecryptElement($encryptedData, $alternateKey, $blacklist);
|
||||||
|
} catch(Exception $t) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Something went wrong during decryption, but for security
|
||||||
|
* reasons we cannot tell the user what failed.
|
||||||
|
*/
|
||||||
|
//print_r($e->getMessage());
|
||||||
|
echo sprintf('Failed to decrypt XML element.');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the metadata of the SP based on the settings
|
||||||
|
*
|
||||||
|
* @param string $sp The SP data
|
||||||
|
* @param string $authnsign authnRequestsSigned attribute
|
||||||
|
* @param string $wsign wantAssertionsSigned attribute
|
||||||
|
* @param DateTime $validUntil Metadata's valid time
|
||||||
|
* @param Timestamp $cacheDuration Duration of the cache in seconds
|
||||||
|
* @param array $contacts Contacts info
|
||||||
|
* @param array $organization Organization ingo
|
||||||
|
*
|
||||||
|
* @return string SAML Metadata XML
|
||||||
|
*/
|
||||||
|
public static function metadata_builder($siteUrl)
|
||||||
|
{
|
||||||
|
$xml = new DOMDocument();
|
||||||
|
$url = plugins_url().'/miniorange-saml-20-single-sign-on/sp-metadata.xml';
|
||||||
|
|
||||||
|
$xml->load($url);
|
||||||
|
|
||||||
|
$xpath = new DOMXPath($xml);
|
||||||
|
$elements = $xpath->query('//md:EntityDescriptor[@entityID="http://{path-to-your-site}/wp-content/plugins/miniorange-saml-20-single-sign-on/"]');
|
||||||
|
|
||||||
|
if ($elements->length >= 1) {
|
||||||
|
$element = $elements->item(0);
|
||||||
|
$element->setAttribute('entityID', $siteUrl.'/wp-content/plugins/miniorange-saml-20-single-sign-on/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$elements = $xpath->query('//md:AssertionConsumerService[@Location="http://{path-to-your-site}"]');
|
||||||
|
if ($elements->length >= 1) {
|
||||||
|
$element = $elements->item(0);
|
||||||
|
$element->setAttribute('Location', $siteUrl.'/');
|
||||||
|
}
|
||||||
|
|
||||||
|
//re-save
|
||||||
|
$xml->save(plugins_url()."/miniorange-saml-20-single-sign-on/sp-metadata.xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_mapped_groups($saml_params, $saml_groups)
|
||||||
|
{
|
||||||
|
$groups = array();
|
||||||
|
|
||||||
|
if (!empty($saml_groups)) {
|
||||||
|
$saml_mapped_groups = array();
|
||||||
|
$i=1;
|
||||||
|
while ($i < 10) {
|
||||||
|
$saml_mapped_groups_value = $saml_params->get('group'.$i.'_map');
|
||||||
|
|
||||||
|
$saml_mapped_groups[$i] = explode(';', $saml_mapped_groups_value);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($saml_groups as $saml_group) {
|
||||||
|
if (!empty($saml_group)) {
|
||||||
|
$i = 0;
|
||||||
|
$found = false;
|
||||||
|
|
||||||
|
while ($i < 9 && !$found) {
|
||||||
|
if (!empty($saml_mapped_groups[$i]) && in_array($saml_group, $saml_mapped_groups[$i], TRUE)) {
|
||||||
|
$groups[] = $saml_params->get('group'.$i);
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_unique($groups);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function getEncryptionAlgorithm($method){
|
||||||
|
switch($method){
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
|
||||||
|
return MoXMLSecurityKey::TRIPLEDES_CBC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
|
||||||
|
return MoXMLSecurityKey::AES128_CBC;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#aes192-cbc':
|
||||||
|
return MoXMLSecurityKey::AES192_CBC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
|
||||||
|
return MoXMLSecurityKey::AES256_CBC;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
|
||||||
|
return MoXMLSecurityKey::RSA_1_5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
|
||||||
|
return MoXMLSecurityKey::RSA_OAEP_MGF1P;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2000/09/xmldsig#dsa-sha1':
|
||||||
|
return MoXMLSecurityKey::DSA_SHA1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2000/09/xmldsig#rsa-sha1':
|
||||||
|
return MoXMLSecurityKey::RSA_SHA1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256':
|
||||||
|
return MoXMLSecurityKey::RSA_SHA256;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384':
|
||||||
|
return MoXMLSecurityKey::RSA_SHA384;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512':
|
||||||
|
return MoXMLSecurityKey::RSA_SHA512;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
echo sprintf('Invalid Encryption Method: '. htmlspecialchars($method));
|
||||||
|
exit;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sanitize_certificate( $certificate ) {
|
||||||
|
$certificate = preg_replace("/[\r\n]+/", "", $certificate);
|
||||||
|
$certificate = str_replace( "-", "", $certificate );
|
||||||
|
$certificate = str_replace( "BEGIN CERTIFICATE", "", $certificate );
|
||||||
|
$certificate = str_replace( "END CERTIFICATE", "", $certificate );
|
||||||
|
$certificate = str_replace( " ", "", $certificate );
|
||||||
|
$certificate = chunk_split($certificate, 64, "\r\n");
|
||||||
|
$certificate = "-----BEGIN CERTIFICATE-----\r\n" . $certificate . "-----END CERTIFICATE-----";
|
||||||
|
return $certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function desanitize_certificate( $certificate ) {
|
||||||
|
$certificate = preg_replace("/[\r\n]+/", "", $certificate);
|
||||||
|
//$certificate = str_replace( "-", "", $certificate );
|
||||||
|
$certificate = str_replace( "-----BEGIN CERTIFICATE-----", "", $certificate );
|
||||||
|
$certificate = str_replace( "-----END CERTIFICATE-----", "", $certificate );
|
||||||
|
$certificate = str_replace( " ", "", $certificate );
|
||||||
|
//$certificate = chunk_split($certificate, 64, "\r\n");
|
||||||
|
//$certificate = "-----BEGIN CERTIFICATE-----\r\n" . $certificate . "-----END CERTIFICATE-----";
|
||||||
|
return $certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mo_saml_wp_remote_post($url, $args = array()){
|
||||||
|
$response = wp_remote_post($url, $args);
|
||||||
|
if(!is_wp_error($response)){
|
||||||
|
return $response;
|
||||||
|
} else {
|
||||||
|
$show_message = new saml_mo_login();
|
||||||
|
update_option('mo_saml_message', __('Unable to connect to the Internet. Please try again.','miniorange-saml-20-single-sign-on'));
|
||||||
|
$show_message->mo_saml_show_error_message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function mo_saml_wp_remote_get($url, $args = array()){
|
||||||
|
$response = wp_remote_get($url, $args);
|
||||||
|
if(!is_wp_error($response)){
|
||||||
|
return $response;
|
||||||
|
} else {
|
||||||
|
$show_message = new saml_mo_login();
|
||||||
|
update_option('mo_saml_message', __('Unable to connect to the Internet. Please try again.','miniorange-saml-20-single-sign-on'));
|
||||||
|
$show_message->mo_saml_show_error_message();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
44
core/lib/saml2/encryption.php
Normal file
44
core/lib/saml2/encryption.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @package miniOrange
|
||||||
|
* @author miniOrange Security Software Pvt. Ltd.
|
||||||
|
* @license GNU/GPLv3
|
||||||
|
* @copyright Copyright 2015 miniOrange. All Rights Reserved.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This file is part of miniOrange SAML plugin.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AESEncryption {
|
||||||
|
/**
|
||||||
|
* @param string $data - the key=value pairs separated with &
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function encrypt_data($data, $key) {
|
||||||
|
$key = openssl_digest($key, 'sha256');
|
||||||
|
$method = 'AES-128-ECB';
|
||||||
|
$ivSize = openssl_cipher_iv_length($method);
|
||||||
|
$iv = openssl_random_pseudo_bytes($ivSize);
|
||||||
|
$strCrypt = openssl_encrypt ($data, $method, $key,OPENSSL_RAW_DATA||OPENSSL_ZERO_PADDING, $iv);
|
||||||
|
return base64_encode($iv.$strCrypt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $data - crypt response from Sagepay
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function decrypt_data($data, $key) {
|
||||||
|
$strIn = base64_decode($data);
|
||||||
|
$key = openssl_digest($key, 'sha256');
|
||||||
|
$method = 'AES-128-ECB';
|
||||||
|
$ivSize = openssl_cipher_iv_length($method);
|
||||||
|
$iv = substr($strIn,0,$ivSize);
|
||||||
|
$data = substr($strIn,$ivSize);
|
||||||
|
$clear = openssl_decrypt ($data, $method, $key, OPENSSL_RAW_DATA||OPENSSL_ZERO_PADDING, $iv);
|
||||||
|
|
||||||
|
return $clear;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
?>
|
||||||
640
core/lib/saml2/mo-saml-options-enum.php
Normal file
640
core/lib/saml2/mo-saml-options-enum.php
Normal file
@@ -0,0 +1,640 @@
|
|||||||
|
<?php
|
||||||
|
include "MoSAMLBasicEnum.php";
|
||||||
|
class mo_saml_options_enum_sso_loginMoSAML extends MoSAMLBasicEnum {
|
||||||
|
const Relay_state = 'mo_saml_relay_state';
|
||||||
|
const Redirect_Idp = 'mo_saml_registered_only_access';
|
||||||
|
const Force_authentication = 'mo_saml_force_authentication';
|
||||||
|
const Enable_access_RSS = 'mo_saml_enable_rss_access';
|
||||||
|
const Auto_redirect = 'mo_saml_enable_login_redirect';
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_enum_identity_providerMoSAML extends MoSAMLBasicEnum{
|
||||||
|
const Broker_service ='mo_saml_enable_cloud_broker';
|
||||||
|
const SP_Base_Url='mo_saml_sp_base_url';
|
||||||
|
const SP_Entity_ID = 'mo_saml_sp_entity_id';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_tab_names extends MoSAMLBasicEnum{
|
||||||
|
|
||||||
|
const Service_provider_settings = 'service-provider-setup';
|
||||||
|
const Identity_provider_settting = 'identity-provider-setup';
|
||||||
|
const Redirection_sso_links = 'redirection-sso-links';
|
||||||
|
const Entire_plugin_tour = 'entire-plugin-tour';
|
||||||
|
const Attribute_role_mapping = 'attribute-role-mapping';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_enum_pointersMoSAML extends MoSAMLBasicEnum{
|
||||||
|
public static
|
||||||
|
$DEFAULT = array(
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-sp-metadata-url',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-select-your-idp',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-upload-metadata',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-test-configuration',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-attribute-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-role-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_default-minorange-use-widget',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-addons',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-support-pointer'
|
||||||
|
);
|
||||||
|
public static $DEFAULT_SKIP = array(
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-sp-metadata-url',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-select-your-idp',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-upload-metadata',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-test-configuration',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-attribute-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-role-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_default-minorange-use-widget',
|
||||||
|
'custom_admin_pointers4_8_52_default-miniorange-addons',
|
||||||
|
);
|
||||||
|
public static $SERVICE_PROVIDER = array(
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-select-your-idp',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-upload-metadata',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-test-configuration',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-import-config',
|
||||||
|
'custom_admin_pointers4_8_52_export-import-config',
|
||||||
|
'custom_admin_pointers4_8_52_configure-service-restart-tour');
|
||||||
|
public static $IDENTITY_PROVIDER = array(
|
||||||
|
'custom_admin_pointers4_8_52_metadata_manual',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-sp-metadata-url',
|
||||||
|
'custom_admin_pointers4_8_52_identity-provider-restart-tour'
|
||||||
|
);
|
||||||
|
public static $ATTRIBUTE_MAPPING = array(
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-attribute-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-role-mapping',
|
||||||
|
'custom_admin_pointers4_8_52_attribute-mapping-restart-tour');
|
||||||
|
public static $REDIRECTION_LINK = array(
|
||||||
|
'custom_admin_pointers4_8_52_minorange-use-widget',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-auto-redirect',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-auto-redirect-login-page',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-short-code',
|
||||||
|
'custom_admin_pointers4_8_52_miniorange-redirection-sso-restart-tour'
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class mo_saml_options_enum_service_providerMoSAML extends MoSAMLBasicEnum{
|
||||||
|
const Identity_name ='saml_identity_name';
|
||||||
|
const Login_binding_type='saml_login_binding_type';
|
||||||
|
const Login_URL = 'saml_login_url';
|
||||||
|
const Logout_binding_type = 'saml_logout_binding_type';
|
||||||
|
const Logout_URL = 'saml_logout_url';
|
||||||
|
const Issuer = 'saml_issuer';
|
||||||
|
const X509_certificate = 'saml_x509_certificate';
|
||||||
|
const Request_signed = 'saml_request_signed';
|
||||||
|
const Guide_name = 'saml_identity_provider_guide_name';
|
||||||
|
const Is_encoding_enabled = 'mo_saml_encoding_enabled';
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_test_configuration extends MoSAMLBasicEnum{
|
||||||
|
const SAML_REQUEST = 'MO_SAML_REQUEST';
|
||||||
|
const SAML_RESPONSE = 'MO_SAML_RESPONSE';
|
||||||
|
const TEST_CONFIG_ERROR_LOG = 'MO_SAML_TEST';
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_enum_attribute_mappingMoSAML extends MoSAMLBasicEnum{
|
||||||
|
const Attribute_Username ='saml_am_username';
|
||||||
|
const Attribute_Email = 'saml_am_email';
|
||||||
|
const Attribute_First_name ='saml_am_first_name';
|
||||||
|
const Attribute_Last_name = 'saml_am_last_name';
|
||||||
|
const Attribute_Group_name ='saml_am_group_name';
|
||||||
|
const Attribute_Custom_mapping = 'mo_saml_custom_attrs_mapping';
|
||||||
|
const Attribute_Account_matcher = 'saml_am_account_matcher';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_enum_role_mappingMoSAML extends MoSAMLBasicEnum{
|
||||||
|
const Role_do_not_auto_create_users = 'mo_saml_dont_create_user_if_role_not_mapped';
|
||||||
|
const Role_do_not_assign_role_unlisted = 'saml_am_dont_allow_unlisted_user_role';
|
||||||
|
const Role_do_not_update_existing_user = 'saml_am_dont_update_existing_user_role';
|
||||||
|
const Role_default_role ='saml_am_default_user_role';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class mo_saml_options_error_constants extends MoSAMLBasicEnum{
|
||||||
|
const Error_no_certificate = "Unable to find a certificate .";
|
||||||
|
const Cause_no_certificate = "No signature found in SAML Response or Assertion. Please sign at least one of them.";
|
||||||
|
const Error_wrong_certificate = "Unable to find a certificate matching the configured fingerprint.";
|
||||||
|
const Cause_wrong_certificate = "X.509 Certificate field in plugin does not match the certificate found in SAML Response.";
|
||||||
|
const Error_invalid_audience = "Invalid Audience URI.";
|
||||||
|
const Cause_invalid_audience = "The value of 'Audience URI' field on Identity Provider's side is incorrect";
|
||||||
|
const Error_issuer_not_verfied = "Issuer cannot be verified.";
|
||||||
|
const Cause_issuer_not_verfied = "IdP Entity ID configured and the one found in SAML Response do not match";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class mo_saml_options_plugin_constants extends MoSAMLBasicEnum{
|
||||||
|
const CMS_Name = "WP";
|
||||||
|
const Application_Name = "WP miniOrange SAML 2.0 SSO Plugin";
|
||||||
|
const Application_type = "SAML";
|
||||||
|
const Version = "4.9.05";
|
||||||
|
const HOSTNAME = "https://login.xecurify.com";
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_plugin_idp extends MoSAMLBasicEnum{
|
||||||
|
public static $IDP_GUIDES = array(
|
||||||
|
"Azure AD" => "azure-ad",
|
||||||
|
"Azure B2C" => "azure-b2c",
|
||||||
|
"ADFS" => "adfs",
|
||||||
|
"Okta" => "okta",
|
||||||
|
"SalesForce" => "salesforce",
|
||||||
|
"Google Apps" => "google-apps",
|
||||||
|
"OneLogin" => "onelogin",
|
||||||
|
"MiniOrange" => "miniorange",
|
||||||
|
"Keycloak" => "jboss-keycloak",
|
||||||
|
"AbsorbLMS" => "absorb-lms",
|
||||||
|
"Degreed" => "degreed",
|
||||||
|
"JumpCloud" => "jumpcloud",
|
||||||
|
"PingFederate" => "pingfederate",
|
||||||
|
"PingOne" => "pingone",
|
||||||
|
"Centrify" => "centrify",
|
||||||
|
"Oracle" => "oracle-enterprise-manager",
|
||||||
|
"Bitium" => "bitium",
|
||||||
|
"Shibboleth 2" => "shibboleth2",
|
||||||
|
"Shibboleth 3" => "shibboleth3",
|
||||||
|
"Gluu Server" => "gluu-server",
|
||||||
|
"SimpleSAMLphp" => "simplesaml",
|
||||||
|
"OpenAM" => "openam",
|
||||||
|
"Authanvil"=>"authanvil",
|
||||||
|
"Auth0"=>"auth0",
|
||||||
|
"CA Identity"=>"ca-identity",
|
||||||
|
"WSO2"=>"wso2",
|
||||||
|
"RSA SecureID"=>"rsa-secureid",
|
||||||
|
"Custom IDP"=>"custom-idp"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_plugin_idp_videos extends MoSAMLBasicEnum{
|
||||||
|
public static $IDP_VIDEOS = array(
|
||||||
|
"azure-ad"=> "eHen4aiflFU",
|
||||||
|
"azure-b2c"=> "",
|
||||||
|
"adfs"=> "rLBHbRbrY5E",
|
||||||
|
"okta"=> "YHE8iYojUqM",
|
||||||
|
"salesforce"=> "LRQrmgr255Q",
|
||||||
|
"google-apps"=> "5BwzEjgZiu4",
|
||||||
|
"onelogin"=> "_Hsot_RG9YY",
|
||||||
|
"miniorange"=> "eamf9s6JpbA",
|
||||||
|
"jboss-keycloak"=> "Io6x1fTNWHI",
|
||||||
|
"absorb-lms"=> "",
|
||||||
|
"degreed"=> "",
|
||||||
|
"jumpcloud"=> "",
|
||||||
|
"pingfederate"=> "",
|
||||||
|
"pingone"=> "",
|
||||||
|
"centrify"=> "",
|
||||||
|
"oracle-enterprise-manager"=> "",
|
||||||
|
"bitium"=> "",
|
||||||
|
"shibboleth2"=> "",
|
||||||
|
"shibboleth3"=> "",
|
||||||
|
"gluu-server"=> "",
|
||||||
|
"simplesaml"=> "",
|
||||||
|
"openam"=> "",
|
||||||
|
"authanvil"=> "",
|
||||||
|
"auth0"=> "54pz6m5h9mk",
|
||||||
|
"ca-identity" => "",
|
||||||
|
"wso2" => "",
|
||||||
|
"rsa-secureid" => "",
|
||||||
|
"custom-idp" => "gilfhNFYsgc"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_options_addons extends MoSAMLBasicEnum{
|
||||||
|
|
||||||
|
public static $ADDON_URL = array(
|
||||||
|
|
||||||
|
'scim' => 'https://plugins.miniorange.com/wordpress-user-provisioning',
|
||||||
|
'page_restriction' => 'https://plugins.miniorange.com/wordpress-page-restriction',
|
||||||
|
'file_prevention' => 'https://plugins.miniorange.com/wordpress-media-restriction',
|
||||||
|
'ssologin' => 'https://plugins.miniorange.com/wordpress-sso-login-audit',
|
||||||
|
'buddypress' => 'https://plugins.miniorange.com/wordpress-buddypress-integrator',
|
||||||
|
'learndash' => 'https://plugins.miniorange.com/wordpress-learndash-integrator',
|
||||||
|
'attribute_based_redirection' => 'https://plugins.miniorange.com/wordpress-attribute-based-redirection-restriction',
|
||||||
|
'ssosession' => 'https://plugins.miniorange.com/sso-session-management',
|
||||||
|
'fsso' => 'https://plugins.miniorange.com/incommon-federation-single-sign-on-sso',
|
||||||
|
'paid_mem_pro' => 'https://plugins.miniorange.com/paid-membership-pro-integrator',
|
||||||
|
'memberpress' => 'https://plugins.miniorange.com/wordpress-memberpress-integrator',
|
||||||
|
'wp_members' => 'https://plugins.miniorange.com/wordpress-members-integrator',
|
||||||
|
'woocommerce' => 'https://plugins.miniorange.com/wordpress-woocommerce-integrator',
|
||||||
|
'guest_login' => 'https://plugins.miniorange.com/guest-user-login',
|
||||||
|
'profile_picture_add_on' => 'https://plugins.miniorange.com/wordpress-profile-picture-map'
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $WP_ADDON_URL = array(
|
||||||
|
'page-restriction' => 'https://wordpress.org/plugins/page-and-post-restriction/embed/',
|
||||||
|
'scim-user-sync'=> 'https://wordpress.org/plugins/scim-user-provisioning/embed/'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $ADDON_TITLE = array(
|
||||||
|
|
||||||
|
'scim' => 'SCIM User Provisioning',
|
||||||
|
'page_restriction' => 'Page and Post Restriction',
|
||||||
|
'file_prevention' => 'Prevent File Access',
|
||||||
|
'ssologin' => 'SSO Login Audit',
|
||||||
|
'buddypress' => 'BuddyPress Integrator',
|
||||||
|
'learndash' => 'Learndash Integrator',
|
||||||
|
'attribute_based_redirection' => 'Attribute Based Redirection',
|
||||||
|
'ssosession' => 'SSO Session Management',
|
||||||
|
'fsso' => 'Federation Single Sign-On',
|
||||||
|
'memberpress' => 'MemberPress Integrator',
|
||||||
|
'wp_members' => 'WP-Members Integrator',
|
||||||
|
'woocommerce' => 'WooCommerce Integrator',
|
||||||
|
'guest_login' => 'Guest Login',
|
||||||
|
'profile_picture_add_on' => 'Profile Picture Add-on',
|
||||||
|
'paid_mem_pro' => 'PaidMembership Pro Integrator'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $RECOMMENDED_ADDONS_PATH = array(
|
||||||
|
|
||||||
|
"learndash" => "sfwd-lms/sfwd_lms.php",
|
||||||
|
"buddypress" => "buddypress/bp-loader.php",
|
||||||
|
"paid_mem_pro" => "paid-memberships-pro/paid-memberships-pro.php",
|
||||||
|
"memberpress" => "memberpress/memberpress.php",
|
||||||
|
"wp_members" => "wp-members/wp-members.php",
|
||||||
|
"woocommerce" => "woocommerce/woocommerce.php"
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_license_plans extends MoSAMLBasicEnum {
|
||||||
|
|
||||||
|
public static $license_plans = array (
|
||||||
|
'standard' => 'WP SAML SSO Standard Plan',
|
||||||
|
'premium' => 'WP SAML SSO Premium Plan',
|
||||||
|
'enterprise' => 'WP SAML SSO Enterprise Plan',
|
||||||
|
'enterprise-multiple-idp' => 'WP SAML SSO Enterprise Multiple-IDP Plan',
|
||||||
|
'all-inclusive' => 'WP SAML SSO All Inclusive Plan',
|
||||||
|
'premium-multisite' => 'WP SAML SSO Premium Multisite Plan',
|
||||||
|
'enterprise-multisite' => 'WP SAML SSO Enterprise Multisite Plan',
|
||||||
|
'all-inclusive-multisite' => 'WP SAML SSO All Inclusive Multisite Plan',
|
||||||
|
'help' => 'Not Sure'
|
||||||
|
);
|
||||||
|
|
||||||
|
public static $license_plans_slug = array (
|
||||||
|
'standard' => '16.0.2@16.0.2',
|
||||||
|
'premium' => '12.0.2@12.0.2',
|
||||||
|
'enterprise' => '12.0.2@12.0.2',
|
||||||
|
'enterprise-multiple-idp' => '25.0.1@25.0.1',
|
||||||
|
'all-inclusive' => '25.0.1@25.0.1',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class mo_saml_time_zones extends MoSAMLBasicEnum {
|
||||||
|
|
||||||
|
public static $time_zones = array(
|
||||||
|
"(GMT-11:00) Niue Time" => "Pacific/Niue",
|
||||||
|
"(GMT-11:00) Samoa Standard Time" => "Pacific/Pago_Pago",
|
||||||
|
"(GMT-10:00) Cook Islands Standard Time" => "Pacific/Rarotonga",
|
||||||
|
"(GMT-10:00) Hawaii-Aleutian Standard Time" => "Pacific/Honolulu",
|
||||||
|
"(GMT-10:00) Tahiti Time" => "Pacific/Tahiti",
|
||||||
|
"(GMT-09:30) Marquesas Time" => "Pacific/Marquesas",
|
||||||
|
"(GMT-09:00) Gambier Time" => "Pacific/Gambier",
|
||||||
|
"(GMT-09:00) Hawaii-Aleutian Time (Adak)" => "America/Adak",
|
||||||
|
"(GMT-08:00) Alaska Time - Anchorage" => "America/Anchorage",
|
||||||
|
"(GMT-08:00) Alaska Time - Juneau" => "America/Juneau",
|
||||||
|
"(GMT-08:00) Alaska Time - Metlakatla" => "America/Metlakatla",
|
||||||
|
"(GMT-08:00) Alaska Time - Nome" => "America/Nome",
|
||||||
|
"(GMT-08:00) Alaska Time - Sitka" => "America/Sitka",
|
||||||
|
"(GMT-08:00) Alaska Time - Yakutat" => "America/Yakutat",
|
||||||
|
"(GMT-08:00) Pitcairn Time" => "Pacific/Pitcairn",
|
||||||
|
"(GMT-07:00) Mexican Pacific Standard Time" => "America/Hermosillo",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Creston" => "America/Creston",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Dawson" => "America/Dawson",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Dawson Creek" => "America/Dawson_Creek",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Fort Nelson" => "America/Fort_Nelson",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Phoenix" => "America/Phoenix",
|
||||||
|
"(GMT-07:00) Mountain Standard Time - Whitehorse" => "America/Whitehorse",
|
||||||
|
"(GMT-07:00) Pacific Time - Los Angeles" => "America/Los_Angeles",
|
||||||
|
"(GMT-07:00) Pacific Time - Tijuana" => "America/Tijuana",
|
||||||
|
"(GMT-07:00) Pacific Time - Vancouver" => "America/Vancouver",
|
||||||
|
"(GMT-06:00) Central Standard Time - Belize" => "America/Belize",
|
||||||
|
"(GMT-06:00) Central Standard Time - Costa Rica" => "America/Costa_Rica",
|
||||||
|
"(GMT-06:00) Central Standard Time - El Salvador" => "America/El_Salvador",
|
||||||
|
"(GMT-06:00) Central Standard Time - Guatemala" => "America/Guatemala",
|
||||||
|
"(GMT-06:00) Central Standard Time - Managua" => "America/Managua",
|
||||||
|
"(GMT-06:00) Central Standard Time - Regina" => "America/Regina",
|
||||||
|
"(GMT-06:00) Central Standard Time - Swift Current" => "America/Swift_Current",
|
||||||
|
"(GMT-06:00) Central Standard Time - Tegucigalpa" => "America/Tegucigalpa",
|
||||||
|
"(GMT-06:00) Easter Island Time" => "Pacific/Easter",
|
||||||
|
"(GMT-06:00) Galapagos Time" => "Pacific/Galapagos",
|
||||||
|
"(GMT-06:00) Mexican Pacific Time - Chihuahua" => "America/Chihuahua",
|
||||||
|
"(GMT-06:00) Mexican Pacific Time - Mazatlan" => "America/Mazatlan",
|
||||||
|
"(GMT-06:00) Mountain Time - Boise" => "America/Boise",
|
||||||
|
"(GMT-06:00) Mountain Time - Cambridge Bay" => "America/Cambridge_Bay",
|
||||||
|
"(GMT-06:00) Mountain Time - Denver" => "America/Denver",
|
||||||
|
"(GMT-06:00) Mountain Time - Edmonton" => "America/Edmonton",
|
||||||
|
"(GMT-06:00) Mountain Time - Inuvik" => "America/Inuvik",
|
||||||
|
"(GMT-06:00) Mountain Time - Ojinaga" => "America/Ojinaga",
|
||||||
|
"(GMT-06:00) Mountain Time - Yellowknife" => "America/Yellowknife",
|
||||||
|
"(GMT-05:00) Acre Standard Time - Eirunepe" => "America/Eirunepe",
|
||||||
|
"(GMT-05:00) Acre Standard Time - Rio Branco" => "America/Rio_Branco",
|
||||||
|
"(GMT-05:00) Central Time - Bahia Banderas" => "America/Bahia_Banderas",
|
||||||
|
"(GMT-05:00) Central Time - Beulah, North Dakota" => "America/North_Dakota/Beulah",
|
||||||
|
"(GMT-05:00) Central Time - Center, North Dakota" => "America/North_Dakota/Center",
|
||||||
|
"(GMT-05:00) Central Time - Chicago" => "America/Chicago",
|
||||||
|
"(GMT-05:00) Central Time - Knox, Indiana" => "America/Indiana/Knox",
|
||||||
|
"(GMT-05:00) Central Time - Matamoros" => "America/Matamoros",
|
||||||
|
"(GMT-05:00) Central Time - Menominee" => "America/Menominee",
|
||||||
|
"(GMT-05:00) Central Time - Merida" => "America/Merida",
|
||||||
|
"(GMT-05:00) Central Time - Mexico City" => "America/Mexico_City",
|
||||||
|
"(GMT-05:00) Central Time - Monterrey" => "America/Monterrey",
|
||||||
|
"(GMT-05:00) Central Time - New Salem, North Dakota" => "America/North_Dakota/New_Salem",
|
||||||
|
"(GMT-05:00) Central Time - Rainy River" => "America/Rainy_River",
|
||||||
|
"(GMT-05:00) Central Time - Rankin Inlet" => "America/Rankin_Inlet",
|
||||||
|
"(GMT-05:00) Central Time - Resolute" => "America/Resolute",
|
||||||
|
"(GMT-05:00) Central Time - Tell City, Indiana" => "America/Indiana/Tell_City",
|
||||||
|
"(GMT-05:00) Central Time - Winnipeg" => "America/Winnipeg",
|
||||||
|
"(GMT-05:00) Colombia Standard Time" => "America/Bogota",
|
||||||
|
"(GMT-05:00) Eastern Standard Time - Atikokan" => "America/Atikokan",
|
||||||
|
"(GMT-05:00) Eastern Standard Time - Cancun" => "America/Cancun",
|
||||||
|
"(GMT-05:00) Eastern Standard Time - Jamaica" => "America/Jamaica",
|
||||||
|
"(GMT-05:00) Eastern Standard Time - Panama" => "America/Panama",
|
||||||
|
"(GMT-05:00) Ecuador Time" => "America/Guayaquil",
|
||||||
|
"(GMT-05:00) Peru Standard Time" => "America/Lima",
|
||||||
|
"(GMT-04:00) Amazon Standard Time - Boa Vista" => "America/Boa_Vista",
|
||||||
|
"(GMT-04:00) Amazon Standard Time - Campo Grande" => "America/Campo_Grande",
|
||||||
|
"(GMT-04:00) Amazon Standard Time - Cuiaba" => "America/Cuiaba",
|
||||||
|
"(GMT-04:00) Amazon Standard Time - Manaus" => "America/Manaus",
|
||||||
|
"(GMT-04:00) Amazon Standard Time - Porto Velho" => "America/Porto_Velho",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Barbados" => "America/Barbados",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Blanc-Sablon" => "America/Blanc-Sablon",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Curaçao" => "America/Curacao",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Martinique" => "America/Martinique",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Port of Spain" => "America/Port_of_Spain",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Puerto Rico" => "America/Puerto_Rico",
|
||||||
|
"(GMT-04:00) Atlantic Standard Time - Santo Domingo" => "America/Santo_Domingo",
|
||||||
|
"(GMT-04:00) Bolivia Time" => "America/La_Paz",
|
||||||
|
"(GMT-04:00) Chile Time" => "America/Santiago",
|
||||||
|
"(GMT-04:00) Cuba Time" => "America/Havana",
|
||||||
|
"(GMT-04:00) Eastern Time - Detroit" => "America/Detroit",
|
||||||
|
"(GMT-04:00) Eastern Time - Grand Turk" => "America/Grand_Turk",
|
||||||
|
"(GMT-04:00) Eastern Time - Indianapolis" => "America/Indiana/Indianapolis",
|
||||||
|
"(GMT-04:00) Eastern Time - Iqaluit" => "America/Iqaluit",
|
||||||
|
"(GMT-04:00) Eastern Time - Louisville" => "America/Kentucky/Louisville",
|
||||||
|
"(GMT-04:00) Eastern Time - Marengo, Indiana" => "America/Indiana/Marengo",
|
||||||
|
"(GMT-04:00) Eastern Time - Monticello, Kentucky" => "America/Kentucky/Monticello",
|
||||||
|
"(GMT-04:00) Eastern Time - Nassau" => "America/Nassau",
|
||||||
|
"(GMT-04:00) Eastern Time - New York" => "America/New_York",
|
||||||
|
"(GMT-04:00) Eastern Time - Nipigon" => "America/Nipigon",
|
||||||
|
"(GMT-04:00) Eastern Time - Pangnirtung" => "America/Pangnirtung",
|
||||||
|
"(GMT-04:00) Eastern Time - Petersburg, Indiana" => "America/Indiana/Petersburg",
|
||||||
|
"(GMT-04:00) Eastern Time - Port-au-Prince" => "America/Port-au-Prince",
|
||||||
|
"(GMT-04:00) Eastern Time - Thunder Bay" => "America/Thunder_Bay",
|
||||||
|
"(GMT-04:00) Eastern Time - Toronto" => "America/Toronto",
|
||||||
|
"(GMT-04:00) Eastern Time - Vevay, Indiana" => "America/Indiana/Vevay",
|
||||||
|
"(GMT-04:00) Eastern Time - Vincennes, Indiana" => "America/Indiana/Vincennes",
|
||||||
|
"(GMT-04:00) Eastern Time - Winamac, Indiana" => "America/Indiana/Winamac",
|
||||||
|
"(GMT-04:00) Guyana Time" => "America/Guyana",
|
||||||
|
"(GMT-04:00) Paraguay Time" => "America/Asuncion",
|
||||||
|
"(GMT-04:00) Venezuela Time" => "America/Caracas",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Buenos Aires" => "America/Argentina/Buenos_Aires",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Catamarca" => "America/Argentina/Catamarca",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Cordoba" => "America/Argentina/Cordoba",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Jujuy" => "America/Argentina/Jujuy",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - La Rioja" => "America/Argentina/La_Rioja",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Mendoza" => "America/Argentina/Mendoza",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Rio Gallegos" => "America/Argentina/Rio_Gallegos",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Salta" => "America/Argentina/Salta",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - San Juan" => "America/Argentina/San_Juan",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - San Luis" => "America/Argentina/San_Luis",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Tucuman" => "America/Argentina/Tucuman",
|
||||||
|
"(GMT-03:00) Argentina Standard Time - Ushuaia" => "America/Argentina/Ushuaia",
|
||||||
|
"(GMT-03:00) Atlantic Time - Bermuda" => "Atlantic/Bermuda",
|
||||||
|
"(GMT-03:00) Atlantic Time - Glace Bay" => "America/Glace_Bay",
|
||||||
|
"(GMT-03:00) Atlantic Time - Goose Bay" => "America/Goose_Bay",
|
||||||
|
"(GMT-03:00) Atlantic Time - Halifax" => "America/Halifax",
|
||||||
|
"(GMT-03:00) Atlantic Time - Moncton" => "America/Moncton",
|
||||||
|
"(GMT-03:00) Atlantic Time - Thule" => "America/Thule",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Araguaina" => "America/Araguaina",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Bahia" => "America/Bahia",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Belem" => "America/Belem",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Fortaleza" => "America/Fortaleza",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Maceio" => "America/Maceio",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Recife" => "America/Recife",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Santarem" => "America/Santarem",
|
||||||
|
"(GMT-03:00) Brasilia Standard Time - Sao Paulo" => "America/Sao_Paulo",
|
||||||
|
"(GMT-03:00) Chile Time" => "America/Santiago",
|
||||||
|
"(GMT-03:00) Falkland Islands Standard Time" => "Atlantic/Stanley",
|
||||||
|
"(GMT-03:00) French Guiana Time" => "America/Cayenne",
|
||||||
|
"(GMT-03:00) Palmer Time" => "Antarctica/Palmer",
|
||||||
|
"(GMT-03:00) Punta Arenas Time" => "America/Punta_Arenas",
|
||||||
|
"(GMT-03:00) Rothera Time" => "Antarctica/Rothera",
|
||||||
|
"(GMT-03:00) Suriname Time" => "America/Paramaribo",
|
||||||
|
"(GMT-03:00) Uruguay Standard Time" => "America/Montevideo",
|
||||||
|
"(GMT-02:30) Newfoundland Time" => "America/St_Johns",
|
||||||
|
"(GMT-02:00) Fernando de Noronha Standard Time" => "America/Noronha",
|
||||||
|
"(GMT-02:00) South Georgia Time" => "Atlantic/South_Georgia",
|
||||||
|
"(GMT-02:00) St. Pierre & Miquelon Time" => "America/Miquelon",
|
||||||
|
"(GMT-02:00) West Greenland Time" => "America/Nuuk",
|
||||||
|
"(GMT-01:00) Cape Verde Standard Time" => "Atlantic/Cape_Verde",
|
||||||
|
"(GMT+00:00) Azores Time" => "Atlantic/Azores",
|
||||||
|
"(GMT+00:00) Coordinated Universal Time" => "UTC",
|
||||||
|
"(GMT+00:00) East Greenland Time" => "America/Scoresbysund",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time" => "Etc/GMT",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Abidjan" => "Africa/Abidjan",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Accra" => "Africa/Accra",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Bissau" => "Africa/Bissau",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Danmarkshavn" => "America/Danmarkshavn",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Monrovia" => "Africa/Monrovia",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - Reykjavik" => "Atlantic/Reykjavik",
|
||||||
|
"(GMT+00:00) Greenwich Mean Time - São Tomé" => "Africa/Sao_Tome",
|
||||||
|
"(GMT+01:00) Central European Standard Time - Algiers" => "Africa/Algiers",
|
||||||
|
"(GMT+01:00) Central European Standard Time - Tunis" => "Africa/Tunis",
|
||||||
|
"(GMT+01:00) Ireland Time" => "Europe/Dublin",
|
||||||
|
"(GMT+01:00) Morocco Time" => "Africa/Casablanca",
|
||||||
|
"(GMT+01:00) United Kingdom Time" => "Europe/London",
|
||||||
|
"(GMT+01:00) West Africa Standard Time - Lagos" => "Africa/Lagos",
|
||||||
|
"(GMT+01:00) West Africa Standard Time - Ndjamena" => "Africa/Ndjamena",
|
||||||
|
"(GMT+01:00) Western European Time - Canary" => "Atlantic/Canary",
|
||||||
|
"(GMT+01:00) Western European Time - Faroe" => "Atlantic/Faroe",
|
||||||
|
"(GMT+01:00) Western European Time - Lisbon" => "Europe/Lisbon",
|
||||||
|
"(GMT+01:00) Western European Time - Madeira" => "Atlantic/Madeira",
|
||||||
|
"(GMT+01:00) Western Sahara Time" => "Africa/El_Aaiun",
|
||||||
|
"(GMT+02:00) Central Africa Time - Khartoum" => "Africa/Khartoum",
|
||||||
|
"(GMT+02:00) Central Africa Time - Maputo" => "Africa/Maputo",
|
||||||
|
"(GMT+02:00) Central Africa Time - Windhoek" => "Africa/Windhoek",
|
||||||
|
"(GMT+02:00) Central European Time - Amsterdam" => "Europe/Amsterdam",
|
||||||
|
"(GMT+02:00) Central European Time - Andorra" => "Europe/Andorra",
|
||||||
|
"(GMT+02:00) Central European Time - Belgrade" => "Europe/Belgrade",
|
||||||
|
"(GMT+02:00) Central European Time - Berlin" => "Europe/Berlin",
|
||||||
|
"(GMT+02:00) Central European Time - Brussels" => "Europe/Brussels",
|
||||||
|
"(GMT+02:00) Central European Time - Budapest" => "Europe/Budapest",
|
||||||
|
"(GMT+02:00) Central European Time - Ceuta" => "Africa/Ceuta",
|
||||||
|
"(GMT+02:00) Central European Time - Copenhagen" => "Europe/Copenhagen",
|
||||||
|
"(GMT+02:00) Central European Time - Gibraltar" => "Europe/Gibraltar",
|
||||||
|
"(GMT+02:00) Central European Time - Luxembourg" => "Europe/Luxembourg",
|
||||||
|
"(GMT+02:00) Central European Time - Madrid" => "Europe/Madrid",
|
||||||
|
"(GMT+02:00) Central European Time - Malta" => "Europe/Malta",
|
||||||
|
"(GMT+02:00) Central European Time - Monaco" => "Europe/Monaco",
|
||||||
|
"(GMT+02:00) Central European Time - Oslo" => "Europe/Oslo",
|
||||||
|
"(GMT+02:00) Central European Time - Paris" => "Europe/Paris",
|
||||||
|
"(GMT+02:00) Central European Time - Prague" => "Europe/Prague",
|
||||||
|
"(GMT+02:00) Central European Time - Rome" => "Europe/Rome",
|
||||||
|
"(GMT+02:00) Central European Time - Stockholm" => "Europe/Stockholm",
|
||||||
|
"(GMT+02:00) Central European Time - Tirane" => "Europe/Tirane",
|
||||||
|
"(GMT+02:00) Central European Time - Vienna" => "Europe/Vienna",
|
||||||
|
"(GMT+02:00) Central European Time - Warsaw" => "Europe/Warsaw",
|
||||||
|
"(GMT+02:00) Central European Time - Zurich" => "Europe/Zurich",
|
||||||
|
"(GMT+02:00) Eastern European Standard Time - Cairo" => "Africa/Cairo",
|
||||||
|
"(GMT+02:00) Eastern European Standard Time - Kaliningrad" => "Europe/Kaliningrad",
|
||||||
|
"(GMT+02:00) Eastern European Standard Time - Tripoli" => "Africa/Tripoli",
|
||||||
|
"(GMT+02:00) South Africa Standard Time" => "Africa/Johannesburg",
|
||||||
|
"(GMT+02:00) Troll Time" => "Antarctica/Troll",
|
||||||
|
"(GMT+03:00) Arabian Standard Time - Baghdad" => "Asia/Baghdad",
|
||||||
|
"(GMT+03:00) Arabian Standard Time - Qatar" => "Asia/Qatar",
|
||||||
|
"(GMT+03:00) Arabian Standard Time - Riyadh" => "Asia/Riyadh",
|
||||||
|
"(GMT+03:00) East Africa Time - Juba" => "Africa/Juba",
|
||||||
|
"(GMT+03:00) East Africa Time - Nairobi" => "Africa/Nairobi",
|
||||||
|
"(GMT+03:00) Eastern European Time - Amman" => "Asia/Amman",
|
||||||
|
"(GMT+03:00) Eastern European Time - Athens" => "Europe/Athens",
|
||||||
|
"(GMT+03:00) Eastern European Time - Beirut" => "Asia/Beirut",
|
||||||
|
"(GMT+03:00) Eastern European Time - Bucharest" => "Europe/Bucharest",
|
||||||
|
"(GMT+03:00) Eastern European Time - Chisinau" => "Europe/Chisinau",
|
||||||
|
"(GMT+03:00) Eastern European Time - Damascus" => "Asia/Damascus",
|
||||||
|
"(GMT+03:00) Eastern European Time - Gaza" => "Asia/Gaza",
|
||||||
|
"(GMT+03:00) Eastern European Time - Hebron" => "Asia/Hebron",
|
||||||
|
"(GMT+03:00) Eastern European Time - Helsinki" => "Europe/Helsinki",
|
||||||
|
"(GMT+03:00) Eastern European Time - Kiev" => "Europe/Kiev",
|
||||||
|
"(GMT+03:00) Eastern European Time - Nicosia" => "Asia/Nicosia",
|
||||||
|
"(GMT+03:00) Eastern European Time - Riga" => "Europe/Riga",
|
||||||
|
"(GMT+03:00) Eastern European Time - Sofia" => "Europe/Sofia",
|
||||||
|
"(GMT+03:00) Eastern European Time - Tallinn" => "Europe/Tallinn",
|
||||||
|
"(GMT+03:00) Eastern European Time - Uzhhorod" => "Europe/Uzhgorod",
|
||||||
|
"(GMT+03:00) Eastern European Time - Vilnius" => "Europe/Vilnius",
|
||||||
|
"(GMT+03:00) Eastern European Time - Zaporozhye" => "Europe/Zaporozhye",
|
||||||
|
"(GMT+03:00) Famagusta Time" => "Asia/Famagusta",
|
||||||
|
"(GMT+03:00) Israel Time" => "Asia/Jerusalem",
|
||||||
|
"(GMT+03:00) Kirov Time" => "Europe/Kirov",
|
||||||
|
"(GMT+03:00) Moscow Standard Time - Minsk" => "Europe/Minsk",
|
||||||
|
"(GMT+03:00) Moscow Standard Time - Moscow" => "Europe/Moscow",
|
||||||
|
"(GMT+03:00) Moscow Standard Time - Simferopol" => "Europe/Simferopol",
|
||||||
|
"(GMT+03:00) Syowa Time" => "Antarctica/Syowa",
|
||||||
|
"(GMT+03:00) Turkey Time" => "Europe/Istanbul",
|
||||||
|
"(GMT+04:00) Armenia Standard Time" => "Asia/Yerevan",
|
||||||
|
"(GMT+04:00) Astrakhan Time" => "Europe/Astrakhan",
|
||||||
|
"(GMT+04:00) Azerbaijan Standard Time" => "Asia/Baku",
|
||||||
|
"(GMT+04:00) Georgia Standard Time" => "Asia/Tbilisi",
|
||||||
|
"(GMT+04:00) Gulf Standard Time" => "Asia/Dubai",
|
||||||
|
"(GMT+04:00) Mauritius Standard Time" => "Indian/Mauritius",
|
||||||
|
"(GMT+04:00) Réunion Time" => "Indian/Reunion",
|
||||||
|
"(GMT+04:00) Samara Standard Time" => "Europe/Samara",
|
||||||
|
"(GMT+04:00) Saratov Time" => "Europe/Saratov",
|
||||||
|
"(GMT+04:00) Seychelles Time" => "Indian/Mahe",
|
||||||
|
"(GMT+04:00) Ulyanovsk Time" => "Europe/Ulyanovsk",
|
||||||
|
"(GMT+04:00) Volgograd Standard Time" => "Europe/Volgograd",
|
||||||
|
"(GMT+04:30) Afghanistan Time" => "Asia/Kabul",
|
||||||
|
"(GMT+04:30) Iran Time" => "Asia/Tehran",
|
||||||
|
"(GMT+05:00) French Southern & Antarctic Time" => "Indian/Kerguelen",
|
||||||
|
"(GMT+05:00) Maldives Time" => "Indian/Maldives",
|
||||||
|
"(GMT+05:00) Mawson Time" => "Antarctica/Mawson",
|
||||||
|
"(GMT+05:00) Pakistan Standard Time" => "Asia/Karachi",
|
||||||
|
"(GMT+05:00) Tajikistan Time" => "Asia/Dushanbe",
|
||||||
|
"(GMT+05:00) Turkmenistan Standard Time" => "Asia/Ashgabat",
|
||||||
|
"(GMT+05:00) Uzbekistan Standard Time - Samarkand" => "Asia/Samarkand",
|
||||||
|
"(GMT+05:00) Uzbekistan Standard Time - Tashkent" => "Asia/Tashkent",
|
||||||
|
"(GMT+05:00) West Kazakhstan Time - Aqtau" => "Asia/Aqtau",
|
||||||
|
"(GMT+05:00) West Kazakhstan Time - Aqtobe" => "Asia/Aqtobe",
|
||||||
|
"(GMT+05:00) West Kazakhstan Time - Atyrau" => "Asia/Atyrau",
|
||||||
|
"(GMT+05:00) West Kazakhstan Time - Oral" => "Asia/Oral",
|
||||||
|
"(GMT+05:00) West Kazakhstan Time - Qyzylorda" => "Asia/Qyzylorda",
|
||||||
|
"(GMT+05:00) Yekaterinburg Standard Time" => "Asia/Yekaterinburg",
|
||||||
|
"(GMT+05:30) Indian Standard Time - Colombo" => "Asia/Colombo",
|
||||||
|
"(GMT+05:30) Indian Standard Time - Kolkata" => "Asia/Kolkata",
|
||||||
|
"(GMT+05:45) Nepal Time" => "Asia/Kathmandu",
|
||||||
|
"(GMT+06:00) Bangladesh Standard Time" => "Asia/Dhaka",
|
||||||
|
"(GMT+06:00) Bhutan Time" => "Asia/Thimphu",
|
||||||
|
"(GMT+06:00) East Kazakhstan Time - Almaty" => "Asia/Almaty",
|
||||||
|
"(GMT+06:00) East Kazakhstan Time - Kostanay" => "Asia/Qostanay",
|
||||||
|
"(GMT+06:00) Indian Ocean Time" => "Indian/Chagos",
|
||||||
|
"(GMT+06:00) Kyrgyzstan Time" => "Asia/Bishkek",
|
||||||
|
"(GMT+06:00) Omsk Standard Time" => "Asia/Omsk",
|
||||||
|
"(GMT+06:00) Urumqi Time" => "Asia/Urumqi",
|
||||||
|
"(GMT+06:00) Vostok Time" => "Antarctica/Vostok",
|
||||||
|
"(GMT+06:30) Cocos Islands Time" => "Indian/Cocos",
|
||||||
|
"(GMT+06:30) Myanmar Time" => "Asia/Yangon",
|
||||||
|
"(GMT+07:00) Barnaul Time" => "Asia/Barnaul",
|
||||||
|
"(GMT+07:00) Christmas Island Time" => "Indian/Christmas",
|
||||||
|
"(GMT+07:00) Davis Time" => "Antarctica/Davis",
|
||||||
|
"(GMT+07:00) Hovd Standard Time" => "Asia/Hovd",
|
||||||
|
"(GMT+07:00) Indochina Time - Bangkok" => "Asia/Bangkok",
|
||||||
|
"(GMT+07:00) Indochina Time - Ho Chi Minh City" => "Asia/Ho_Chi_Minh",
|
||||||
|
"(GMT+07:00) Krasnoyarsk Standard Time - Krasnoyarsk" => "Asia/Krasnoyarsk",
|
||||||
|
"(GMT+07:00) Krasnoyarsk Standard Time - Novokuznetsk" => "Asia/Novokuznetsk",
|
||||||
|
"(GMT+07:00) Novosibirsk Standard Time" => "Asia/Novosibirsk",
|
||||||
|
"(GMT+07:00) Tomsk Time" => "Asia/Tomsk",
|
||||||
|
"(GMT+07:00) Western Indonesia Time - Jakarta" => "Asia/Jakarta",
|
||||||
|
"(GMT+07:00) Western Indonesia Time - Pontianak" => "Asia/Pontianak",
|
||||||
|
"(GMT+08:00) Australian Western Standard Time - Casey" => "Antarctica/Casey",
|
||||||
|
"(GMT+08:00) Australian Western Standard Time - Perth" => "Australia/Perth",
|
||||||
|
"(GMT+08:00) Brunei Darussalam Time" => "Asia/Brunei",
|
||||||
|
"(GMT+08:00) Central Indonesia Time" => "Asia/Makassar",
|
||||||
|
"(GMT+08:00) China Standard Time - Macao" => "Asia/Macau",
|
||||||
|
"(GMT+08:00) China Standard Time - Shanghai" => "Asia/Shanghai",
|
||||||
|
"(GMT+08:00) Hong Kong Standard Time" => "Asia/Hong_Kong",
|
||||||
|
"(GMT+08:00) Irkutsk Standard Time" => "Asia/Irkutsk",
|
||||||
|
"(GMT+08:00) Malaysia Time - Kuala Lumpur" => "Asia/Kuala_Lumpur",
|
||||||
|
"(GMT+08:00) Malaysia Time - Kuching" => "Asia/Kuching",
|
||||||
|
"(GMT+08:00) Philippine Standard Time" => "Asia/Manila",
|
||||||
|
"(GMT+08:00) Singapore Standard Time" => "Asia/Singapore",
|
||||||
|
"(GMT+08:00) Taipei Standard Time" => "Asia/Taipei",
|
||||||
|
"(GMT+08:00) Ulaanbaatar Standard Time - Choibalsan" => "Asia/Choibalsan",
|
||||||
|
"(GMT+08:00) Ulaanbaatar Standard Time - Ulaanbaatar" => "Asia/Ulaanbaatar",
|
||||||
|
"(GMT+08:45) Australian Central Western Standard Time" => "Australia/Eucla",
|
||||||
|
"(GMT+09:00) East Timor Time" => "Asia/Dili",
|
||||||
|
"(GMT+09:00) Eastern Indonesia Time" => "Asia/Jayapura",
|
||||||
|
"(GMT+09:00) Japan Standard Time" => "Asia/Tokyo",
|
||||||
|
"(GMT+09:00) Korean Standard Time - Pyongyang" => "Asia/Pyongyang",
|
||||||
|
"(GMT+09:00) Korean Standard Time - Seoul" => "Asia/Seoul",
|
||||||
|
"(GMT+09:00) Palau Time" => "Pacific/Palau",
|
||||||
|
"(GMT+09:00) Yakutsk Standard Time - Chita" => "Asia/Chita",
|
||||||
|
"(GMT+09:00) Yakutsk Standard Time - Khandyga" => "Asia/Khandyga",
|
||||||
|
"(GMT+09:00) Yakutsk Standard Time - Yakutsk" => "Asia/Yakutsk",
|
||||||
|
"(GMT+09:30) Australian Central Standard Time" => "Australia/Darwin",
|
||||||
|
"(GMT+09:30) Central Australia Time - Adelaide" => "Australia/Adelaide",
|
||||||
|
"(GMT+09:30) Central Australia Time - Broken Hill" => "Australia/Broken_Hill",
|
||||||
|
"(GMT+10:00) Australian Eastern Standard Time - Brisbane" => "Australia/Brisbane",
|
||||||
|
"(GMT+10:00) Australian Eastern Standard Time - Lindeman" => "Australia/Lindeman",
|
||||||
|
"(GMT+10:00) Chamorro Standard Time" => "Pacific/Guam",
|
||||||
|
"(GMT+10:00) Chuuk Time" => "Pacific/Chuuk",
|
||||||
|
"(GMT+10:00) Dumont-d’Urville Time" => "Antarctica/DumontDUrville",
|
||||||
|
"(GMT+10:00) Eastern Australia Time - Currie" => "Australia/Currie",
|
||||||
|
"(GMT+10:00) Eastern Australia Time - Hobart" => "Australia/Hobart",
|
||||||
|
"(GMT+10:00) Eastern Australia Time - Melbourne" => "Australia/Melbourne",
|
||||||
|
"(GMT+10:00) Eastern Australia Time - Sydney" => "Australia/Sydney",
|
||||||
|
"(GMT+10:00) Papua New Guinea Time" => "Pacific/Port_Moresby",
|
||||||
|
"(GMT+10:00) Vladivostok Standard Time - Ust-Nera" => "Asia/Ust-Nera",
|
||||||
|
"(GMT+10:00) Vladivostok Standard Time - Vladivostok" => "Asia/Vladivostok",
|
||||||
|
"(GMT+10:30) Lord Howe Time" => "Australia/Lord_Howe",
|
||||||
|
"(GMT+11:00) Bougainville Time" => "Pacific/Bougainville",
|
||||||
|
"(GMT+11:00) Kosrae Time" => "Pacific/Kosrae",
|
||||||
|
"(GMT+11:00) Macquarie Island Time" => "Antarctica/Macquarie",
|
||||||
|
"(GMT+11:00) Magadan Standard Time" => "Asia/Magadan",
|
||||||
|
"(GMT+11:00) New Caledonia Standard Time" => "Pacific/Noumea",
|
||||||
|
"(GMT+11:00) Norfolk Island Time" => "Pacific/Norfolk",
|
||||||
|
"(GMT+11:00) Ponape Time" => "Pacific/Pohnpei",
|
||||||
|
"(GMT+11:00) Sakhalin Standard Time" => "Asia/Sakhalin",
|
||||||
|
"(GMT+11:00) Solomon Islands Time" => "Pacific/Guadalcanal",
|
||||||
|
"(GMT+11:00) Srednekolymsk Time" => "Asia/Srednekolymsk",
|
||||||
|
"(GMT+11:00) Vanuatu Standard Time" => "Pacific/Efate",
|
||||||
|
"(GMT+12:00) Anadyr Standard Time" => "Asia/Anadyr",
|
||||||
|
"(GMT+12:00) Fiji Time" => "Pacific/Fiji",
|
||||||
|
"(GMT+12:00) Gilbert Islands Time" => "Pacific/Tarawa",
|
||||||
|
"(GMT+12:00) Marshall Islands Time - Kwajalein" => "Pacific/Kwajalein",
|
||||||
|
"(GMT+12:00) Marshall Islands Time - Majuro" => "Pacific/Majuro",
|
||||||
|
"(GMT+12:00) Nauru Time" => "Pacific/Nauru",
|
||||||
|
"(GMT+12:00) New Zealand Time" => "Pacific/Auckland",
|
||||||
|
"(GMT+12:00) Petropavlovsk-Kamchatski Standard Time" => "Asia/Kamchatka",
|
||||||
|
"(GMT+12:00) Tuvalu Time" => "Pacific/Funafuti",
|
||||||
|
"(GMT+12:00) Wake Island Time" => "Pacific/Wake",
|
||||||
|
"(GMT+12:00) Wallis & Futuna Time" => "Pacific/Wallis",
|
||||||
|
"(GMT+12:45) Chatham Time" => "Pacific/Chatham",
|
||||||
|
"(GMT+13:00) Apia Time" => "Pacific/Apia",
|
||||||
|
"(GMT+13:00) Phoenix Islands Time" => "Pacific/Enderbury",
|
||||||
|
"(GMT+13:00) Tokelau Time" => "Pacific/Fakaofo",
|
||||||
|
"(GMT+13:00) Tonga Standard Time" => "Pacific/Tongatapu",
|
||||||
|
"(GMT+14:00) Line Islands Time" => "Pacific/Kiritimati"
|
||||||
|
);
|
||||||
|
}
|
||||||
47
core/lib/saml2/xmlseclibs.php
Normal file
47
core/lib/saml2/xmlseclibs.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* xmlseclibs.php
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2016, Robert Richards <rrichards@cdatazone.org>.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in
|
||||||
|
* the documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* * Neither the name of Robert Richards nor the names of his
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||||
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @author Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @copyright 2007-2017 Robert Richards <rrichards@cdatazone.org>
|
||||||
|
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||||
|
* @version 3.0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
$xmlseclibs_srcdir = dirname(__FILE__) . '/SAML2Core';
|
||||||
|
require $xmlseclibs_srcdir . '/MoXMLSecurityKey.php';
|
||||||
|
require $xmlseclibs_srcdir . '/MoXMLSecurityDSig.php';
|
||||||
|
require $xmlseclibs_srcdir . '/MoXMLSecEnc.php';
|
||||||
|
require $xmlseclibs_srcdir . '/Utils/MoXPath.php';
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
define('CLIENT_PATH', dirname(__FILE__));
|
use Classes\SAMLManager;use Classes\SettingsManager;use Utils\LogManager;define('CLIENT_PATH', dirname(__FILE__));
|
||||||
include("config.base.php");
|
include("config.base.php");
|
||||||
include("include.common.php");
|
include("include.common.php");
|
||||||
include("server.includes.inc.php");
|
include("server.includes.inc.php");
|
||||||
|
|
||||||
$companyName = \Classes\SettingsManager::getInstance()->getSetting('Company: Name');
|
$gsuiteEnabled = SettingsManager::getInstance()->getSetting('System: G Suite Enabled');
|
||||||
$gsuiteEnabled = \Classes\SettingsManager::getInstance()->getSetting('System: G Suite Enabled');
|
$companyName = SettingsManager::getInstance()->getSetting('Company: Name');
|
||||||
|
$SAMLAutoLogin = SettingsManager::getInstance()->getSetting('SAML: Auto Login') === "1";
|
||||||
|
$SAMLEnabled = SettingsManager::getInstance()->getSetting("SAML: Enabled") == "1";
|
||||||
|
$SAMLUserLoaded = false;
|
||||||
|
|
||||||
if (isset($_REQUEST['logout'])) {
|
if (isset($_REQUEST['logout'])) {
|
||||||
\Utils\SessionUtils::unsetClientSession();
|
\Utils\SessionUtils::unsetClientSession();
|
||||||
@@ -14,12 +17,19 @@ if (isset($_REQUEST['logout'])) {
|
|||||||
|
|
||||||
if (empty($user) || empty($user->email)) {
|
if (empty($user) || empty($user->email)) {
|
||||||
|
|
||||||
if (!empty($_REQUEST['username']) && !empty($_REQUEST['password'])) {
|
if (!isset($_REQUEST['logout']) && !isset($_POST['SAMLResponse']) && $SAMLAutoLogin && $SAMLEnabled && !empty(SettingsManager::getInstance()->getSetting("SAML: IDP SSO Url"))) {
|
||||||
|
header("Location:" . SettingsManager::getInstance()->getSetting("SAML: IDP SSO Url"));
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!empty($_REQUEST['username']) && !empty($_REQUEST['password']))
|
||||||
|
|| isset($_POST['SAMLResponse'])
|
||||||
|
) {
|
||||||
$suser = null;
|
$suser = null;
|
||||||
$ssoUserLoaded = false;
|
$ssoUserLoaded = false;
|
||||||
|
|
||||||
if($_REQUEST['username'] != "admin") {
|
if($_REQUEST['username'] != "admin") {
|
||||||
if (\Classes\SettingsManager::getInstance()->getSetting("LDAP: Enabled") == "1") {
|
if (SettingsManager::getInstance()->getSetting("LDAP: Enabled") === "1") {
|
||||||
$ldapResp = \Classes\LDAPManager::getInstance()->checkLDAPLogin($_REQUEST['username'], $_REQUEST['password']);
|
$ldapResp = \Classes\LDAPManager::getInstance()->checkLDAPLogin($_REQUEST['username'], $_REQUEST['password']);
|
||||||
if ($ldapResp->getStatus() == \Classes\IceResponse::ERROR) {
|
if ($ldapResp->getStatus() == \Classes\IceResponse::ERROR) {
|
||||||
header("Location:" . CLIENT_BASE_URL . "login.php?f=1");
|
header("Location:" . CLIENT_BASE_URL . "login.php?f=1");
|
||||||
@@ -36,6 +46,39 @@ if (empty($user) || empty($user->email)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($SAMLEnabled && isset($_POST['SAMLResponse'])) {
|
||||||
|
$samlData = $_POST['SAMLResponse'];
|
||||||
|
|
||||||
|
if(array_key_exists('RelayState', $_POST) && !empty( $_POST['RelayState'] ) && $_POST['RelayState'] !== '/') {
|
||||||
|
$relayState = htmlspecialchars($_POST['RelayState']);
|
||||||
|
} else {
|
||||||
|
$relayState = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$ssoUserEmail = (new SAMLManager())->getSSOEmail($samlData, $relayState);
|
||||||
|
LogManager::getInstance()->info('SSO SAML User Email:'.$ssoUserEmail);
|
||||||
|
if (false === $ssoUserEmail) {
|
||||||
|
header("Location:" . CLIENT_BASE_URL . "login.php?f=1");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$mapping = SettingsManager::getInstance()->getSetting('SAML: Name ID Mapping');
|
||||||
|
$suser = new \Users\Common\Model\User();
|
||||||
|
if ($mapping === 'username') {
|
||||||
|
$suser->Load("username = ?", array($ssoUserEmail));
|
||||||
|
} else {
|
||||||
|
$suser->Load("email = ?", array($ssoUserEmail));
|
||||||
|
}
|
||||||
|
|
||||||
|
LogManager::getInstance()->info('SSO SAML User:'.print_r($suser, true));
|
||||||
|
if (empty($suser)) {
|
||||||
|
header("Location:" . CLIENT_BASE_URL . "login.php?f=1");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
$ssoUserLoaded = true;
|
||||||
|
$SAMLUserLoaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($suser)) {
|
if (empty($suser)) {
|
||||||
$suser = new \Users\Common\Model\User();
|
$suser = new \Users\Common\Model\User();
|
||||||
$suser->Load(
|
$suser->Load(
|
||||||
@@ -59,7 +102,7 @@ if (empty($user) || empty($user->email)) {
|
|||||||
|
|
||||||
$loginCsrf = \Utils\SessionUtils::getSessionObject('csrf-login');
|
$loginCsrf = \Utils\SessionUtils::getSessionObject('csrf-login');
|
||||||
|
|
||||||
if ($_REQUEST['csrf'] != $loginCsrf || empty($_REQUEST['csrf'])) {
|
if (!$SAMLUserLoaded && ($_REQUEST['csrf'] != $loginCsrf || empty($_REQUEST['csrf']))) {
|
||||||
$next = !empty($_REQUEST['next'])?'&next='.$_REQUEST['next']:'';
|
$next = !empty($_REQUEST['next'])?'&next='.$_REQUEST['next']:'';
|
||||||
header("Location:".CLIENT_BASE_URL."login.php?f=1".$next);
|
header("Location:".CLIENT_BASE_URL."login.php?f=1".$next);
|
||||||
exit();
|
exit();
|
||||||
@@ -277,7 +320,7 @@ $csrfToken = sha1(rand(4500, 100000) . time(). CLIENT_BASE_URL);
|
|||||||
<div class="col-lg-6 col-md-8 col-xs-10">
|
<div class="col-lg-6 col-md-8 col-xs-10">
|
||||||
<div class="bg-white-2 h-100 px-11 pt-11 pb-7">
|
<div class="bg-white-2 h-100 px-11 pt-11 pb-7">
|
||||||
<div class="row d-flex justify-content-center">
|
<div class="row d-flex justify-content-center">
|
||||||
<img src="<?=$logoFileUrl?>" style="max-width:100%;max-height:280px;"/>
|
<img style="max-width: 100%;" src="<?=$logoFileUrl?>"/>
|
||||||
</div>
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<?php if ($gsuiteEnabled) {?>
|
<?php if ($gsuiteEnabled) {?>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
$migrationList = [];
|
$migrationList = [];
|
||||||
|
$migrationList[] = 'v20210402_280006_modify_attendance_rep';
|
||||||
|
$migrationList[] = 'v20210327_280005_saml_settings';
|
||||||
|
$migrationList[] = 'v20210228_280003_add_share_with_employee';
|
||||||
|
$migrationList[] = 'v20210228_280004_add_visible_to_document';
|
||||||
$migrationList[] = 'v20201028_280002_update_gender';
|
$migrationList[] = 'v20201028_280002_update_gender';
|
||||||
$migrationList[] = 'v20201028_280001_update_module_names';
|
$migrationList[] = 'v20201028_280001_update_module_names';
|
||||||
$migrationList[] = 'v20201017_271101_switch_off_photo_att';
|
$migrationList[] = 'v20201017_271101_switch_off_photo_att';
|
||||||
|
|||||||
24
core/migrations/v20210228_280003_add_share_with_employee.php
Normal file
24
core/migrations/v20210228_280003_add_share_with_employee.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Classes\Migration;
|
||||||
|
|
||||||
|
class v20210228_280003_add_share_with_employee extends AbstractMigration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
|
||||||
|
$sql = <<<'SQL'
|
||||||
|
ALTER TABLE `Documents`
|
||||||
|
ADD COLUMN `share_with_employee` enum('Yes','No') NULL DEFAULT 'Yes' AFTER `updated`;
|
||||||
|
SQL;
|
||||||
|
$this->executeQuery($sql);
|
||||||
|
|
||||||
|
return $this->executeQuery($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
core/migrations/v20210228_280004_add_visible_to_document.php
Normal file
24
core/migrations/v20210228_280004_add_visible_to_document.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Classes\Migration;
|
||||||
|
|
||||||
|
class v20210228_280004_add_visible_to_document extends AbstractMigration
|
||||||
|
{
|
||||||
|
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
|
||||||
|
$sql = <<<'SQL'
|
||||||
|
ALTER TABLE `EmployeeDocuments`
|
||||||
|
ADD COLUMN `visible_to` enum('Owner','Manager','Admin') NULL DEFAULT 'Owner' AFTER `expire_notification_last`;
|
||||||
|
SQL;
|
||||||
|
$this->executeQuery($sql);
|
||||||
|
|
||||||
|
return $this->executeQuery($sql);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
core/migrations/v20210327_280005_saml_settings.php
Normal file
85
core/migrations/v20210327_280005_saml_settings.php
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
namespace Classes\Migration;
|
||||||
|
|
||||||
|
class v20210327_280005_saml_settings extends AbstractMigration {
|
||||||
|
|
||||||
|
public function up(){
|
||||||
|
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: Enabled',
|
||||||
|
'0',
|
||||||
|
'Enable SAML Login',
|
||||||
|
'["value", {"label":"Value","type":"select","source":[["1","Yes"],["0","No"]]}]',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: Auto Login',
|
||||||
|
'0',
|
||||||
|
'Try to login via SAML by redirecting the user to SSO Url',
|
||||||
|
'["value", {"label":"Value","type":"select","source":[["1","Yes"],["0","No"]]}]',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: IDP SSO Url',
|
||||||
|
'',
|
||||||
|
'Identity Provider Single Sign-On URL. Users will be redirected to this URL for authentication',
|
||||||
|
'',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: IDP Issuer',
|
||||||
|
'',
|
||||||
|
'Identity Provider Issuer',
|
||||||
|
'',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: X.509 Certificate',
|
||||||
|
'',
|
||||||
|
'X.509 Certificate provided by the Identity Provider. This certificate will be encrypted',
|
||||||
|
'["value", {"label":"Value","type":"textarea"}]',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$sql[] = <<<'SQL'
|
||||||
|
REPLACE INTO `Settings` (`name`, `value`, `description`, `meta`, `category`)
|
||||||
|
VALUES (
|
||||||
|
'SAML: Name ID Mapping',
|
||||||
|
'email',
|
||||||
|
'SAML Name id mapped to can be mapped to icehrm user email or the username',
|
||||||
|
'["value", {"label":"Value","type":"select","source":[["email","Email"],["username","Username"]]}]',
|
||||||
|
'SAML'
|
||||||
|
);
|
||||||
|
SQL;
|
||||||
|
|
||||||
|
$result = true;
|
||||||
|
foreach ($sql as $query) {
|
||||||
|
$result = $result && $this->executeQuery($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
32
core/migrations/v20210402_280006_modify_attendance_rep.php
Normal file
32
core/migrations/v20210402_280006_modify_attendance_rep.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace Classes\Migration;
|
||||||
|
|
||||||
|
use Model\Report;
|
||||||
|
|
||||||
|
class v20210402_280006_modify_attendance_rep extends AbstractMigration {
|
||||||
|
|
||||||
|
public function up(){
|
||||||
|
|
||||||
|
$report = new Report();
|
||||||
|
$report->Load('name = ?', ['Employee Time Tracking Report']);
|
||||||
|
$report->parameters = <<<'JSON'
|
||||||
|
[
|
||||||
|
[ "employee", {"label":"Employee","type":"select2","allow-null":false,"remote-source":["Employee","id","first_name+last_name"]}],
|
||||||
|
[ "date_start", {"label":"Start Date","type":"date", "validation":"none"}],
|
||||||
|
[ "date_end", {"label":"End Date","type":"date","validation":"none"}],
|
||||||
|
["period", { "label": "Period", "type": "select", "source": [["Current Month", "Current Month"], ["Last Month", "Last Month"], ["Last Week", "Last Week"], ["Last 2 Weeks", "Last 2 Weeks"]], "validation":"none" }]
|
||||||
|
]
|
||||||
|
JSON;
|
||||||
|
$report->paramOrder = <<<'JSON'
|
||||||
|
["employee","date_start","date_end","period"]
|
||||||
|
JSON;
|
||||||
|
|
||||||
|
$report->Save();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -291,6 +291,12 @@ foreach ($ams as $am) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$extensionManager = new \Classes\ExtensionManager();
|
||||||
|
$extensionData = $extensionManager->setupExtensions();
|
||||||
|
$extensionIcons = $extensionData[0];
|
||||||
|
$extensionTemp = $extensionData[1];
|
||||||
|
$extensionMenus = array_keys($extensionIcons);
|
||||||
|
|
||||||
foreach ($adminModulesTemp as $k => $v) {
|
foreach ($adminModulesTemp as $k => $v) {
|
||||||
ksort($adminModulesTemp[$k]);
|
ksort($adminModulesTemp[$k]);
|
||||||
}
|
}
|
||||||
@@ -299,6 +305,10 @@ foreach ($userModulesTemp as $k => $v) {
|
|||||||
ksort($userModulesTemp[$k]);
|
ksort($userModulesTemp[$k]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($extensionTemp as $k => $v) {
|
||||||
|
ksort($extensionTemp[$k]);
|
||||||
|
}
|
||||||
|
|
||||||
$adminIcons = json_decode(file_get_contents(CLIENT_PATH.'/admin/meta.json'), true);
|
$adminIcons = json_decode(file_get_contents(CLIENT_PATH.'/admin/meta.json'), true);
|
||||||
$adminMenus = array_keys($adminIcons);
|
$adminMenus = array_keys($adminIcons);
|
||||||
|
|
||||||
@@ -332,8 +342,6 @@ foreach ($userMenus as $menu) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$mainIcons = array_merge($adminIcons, $userIcons);
|
|
||||||
|
|
||||||
foreach ($userModulesTemp as $k => $v) {
|
foreach ($userModulesTemp as $k => $v) {
|
||||||
if (!in_array($k, $added)) {
|
if (!in_array($k, $added)) {
|
||||||
$arr = array("name"=>$k,"menu"=>$userModulesTemp[$k]);
|
$arr = array("name"=>$k,"menu"=>$userModulesTemp[$k]);
|
||||||
@@ -341,6 +349,25 @@ foreach ($userModulesTemp as $k => $v) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$extensions = array();
|
||||||
|
foreach ($extensionMenus as $menu) {
|
||||||
|
if (isset($extensionTemp[$menu])) {
|
||||||
|
$arr = array("name"=>$menu,"menu"=>$extensionTemp[$menu]);
|
||||||
|
$extensions[] = $arr;
|
||||||
|
$added[] = $menu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($extensionTemp as $k => $v) {
|
||||||
|
if (!in_array($k, $added)) {
|
||||||
|
$arr = array("name"=>$k,"menu"=>$extensionTemp[$k]);
|
||||||
|
$extensions[] = $arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge icons
|
||||||
|
$mainIcons = array_merge($adminIcons, $userIcons, $extensionIcons);
|
||||||
|
|
||||||
//Remove modules having no permissions
|
//Remove modules having no permissions
|
||||||
if (!empty($user)) {
|
if (!empty($user)) {
|
||||||
if (!empty($user->user_roles)) {
|
if (!empty($user->user_roles)) {
|
||||||
@@ -393,4 +420,24 @@ if (!empty($user)) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($extensions as $fk => $menu) {
|
||||||
|
foreach ($menu['menu'] as $key => $item) {
|
||||||
|
// If the user's once of the user roles are blacklisted for the module
|
||||||
|
$commonRoles = array_intersect($item['user_roles_blacklist'], $userRoles);
|
||||||
|
if (!empty($commonRoles)) {
|
||||||
|
unset($extensions[$fk]['menu'][$key]);
|
||||||
|
}
|
||||||
|
if (!in_array($user->user_level, $item['user_levels'])) {
|
||||||
|
if (!empty($userRoles)) {
|
||||||
|
$commonRoles = array_intersect($item['user_roles'], $userRoles);
|
||||||
|
if (empty($commonRoles)) {
|
||||||
|
unset($extensions[$fk]['menu'][$key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unset($extensions[$fk]['menu'][$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ modJsList['tabEmployeeDocument'].setShowDelete(false);
|
|||||||
modJsList['tabEmployeeDocument'].setShowEdit(false);
|
modJsList['tabEmployeeDocument'].setShowEdit(false);
|
||||||
<?php }?>
|
<?php }?>
|
||||||
|
|
||||||
|
modJsList['tabEmployeeDocument'].setRemoteTable(true);
|
||||||
|
|
||||||
|
|
||||||
modJsList['tabEmployeeCompanyDocument'] = new EmployeeCompanyDocumentAdapter('CompanyDocument','EmployeeCompanyDocument');
|
modJsList['tabEmployeeCompanyDocument'] = new EmployeeCompanyDocumentAdapter('CompanyDocument','EmployeeCompanyDocument');
|
||||||
modJsList['tabEmployeeCompanyDocument'].setLoadMoreButton($("#loadMoreEmployeeCompanyDocument"));
|
modJsList['tabEmployeeCompanyDocument'].setLoadMoreButton($("#loadMoreEmployeeCompanyDocument"));
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Loans\Common\Model\EmployeeCompanyLoan;
|
||||||
|
|
||||||
$moduleName = 'loans';
|
$moduleName = 'loans';
|
||||||
$moduleGroup = 'modules';
|
$moduleGroup = 'modules';
|
||||||
define('MODULE_PATH',dirname(__FILE__));
|
define('MODULE_PATH',dirname(__FILE__));
|
||||||
@@ -17,26 +20,27 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="tabPageEmployeeCompanyLoan">
|
<div class="tab-pane active" id="tabPageEmployeeCompanyLoan">
|
||||||
<div id="EmployeeCompanyLoan" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div id="EmployeeCompanyLoanForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
|
<div id="EmployeeCompanyLoanTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="EmployeeCompanyLoanForm"></div>
|
||||||
|
<div id="EmployeeCompanyLoanFilterForm"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'EmployeeCompanyLoan' => PermissionManager::checkGeneralAccess(new EmployeeCompanyLoan()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var modJsList = new Array();
|
initAdminLoan(<?=json_encode($moduleData)?>);
|
||||||
|
|
||||||
modJsList['tabEmployeeCompanyLoan'] = new EmployeeCompanyLoanAdapter('EmployeeCompanyLoan','EmployeeCompanyLoan');
|
|
||||||
modJsList['tabEmployeeCompanyLoan'].setShowAddNew(false);
|
|
||||||
modJsList['tabEmployeeCompanyLoan'].setShowSave(false);
|
|
||||||
modJsList['tabEmployeeCompanyLoan'].setShowDelete(false);
|
|
||||||
modJsList['tabEmployeeCompanyLoan'].setShowEdit(true);
|
|
||||||
|
|
||||||
var modJs = modJsList['tabEmployeeCompanyLoan'];
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php include APP_BASE_PATH.'footer.php';?>
|
<?php include APP_BASE_PATH.'footer.php';?>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
use Salary\Common\Model\EmployeeSalary;
|
||||||
|
|
||||||
$moduleName = 'salary';
|
$moduleName = 'salary';
|
||||||
$moduleGroup = 'modules';
|
$moduleGroup = 'modules';
|
||||||
define('MODULE_PATH',dirname(__FILE__));
|
define('MODULE_PATH',dirname(__FILE__));
|
||||||
@@ -17,32 +20,25 @@ include APP_BASE_PATH.'modulejslibs.inc.php';
|
|||||||
|
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="tabPageEmployeeSalary">
|
<div class="tab-pane active" id="tabPageEmployeeSalary">
|
||||||
<div id="EmployeeSalary" class="reviewBlock" data-content="List" style="padding-left:5px;">
|
<div id="EmployeeSalaryTable" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>
|
||||||
|
<div id="EmployeeSalaryForm"></div>
|
||||||
</div>
|
<div id="EmployeeSalaryFilterForm"></div>
|
||||||
<div id="EmployeeSalaryForm" class="reviewBlock" data-content="Form" style="padding-left:5px;display:none;">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
'EmployeeSalary' => PermissionManager::checkGeneralAccess(new EmployeeSalary()),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var modJsList = new Array();
|
initAdminSalary(<?=json_encode($moduleData)?>);
|
||||||
|
|
||||||
modJsList['tabEmployeeSalary'] = new EmployeeSalaryAdapter('EmployeeSalary');
|
|
||||||
|
|
||||||
<?php if(isset($modulePermissions['perm']['Add Salary']) && $modulePermissions['perm']['Add Salary'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeSalary'].setShowAddNew(false);
|
|
||||||
<?php }?>
|
|
||||||
<?php if(isset($modulePermissions['perm']['Delete Salary']) && $modulePermissions['perm']['Delete Salary'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeSalary'].setShowDelete(false);
|
|
||||||
<?php }?>
|
|
||||||
<?php if(isset($modulePermissions['perm']['Edit Salary']) && $modulePermissions['perm']['Edit Salary'] == "No"){?>
|
|
||||||
modJsList['tabEmployeeSalary'].setShowEdit(false);
|
|
||||||
<?php }?>
|
|
||||||
|
|
||||||
var modJs = modJsList['tabEmployeeSalary'];
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<?php include APP_BASE_PATH.'footer.php';?>
|
<?php include APP_BASE_PATH.'footer.php';?>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use Classes\Migration\MigrationManager;
|
|||||||
use Classes\NotificationManager;
|
use Classes\NotificationManager;
|
||||||
use Classes\ReportHandler;
|
use Classes\ReportHandler;
|
||||||
use Classes\SettingsManager;
|
use Classes\SettingsManager;
|
||||||
|
use Model\BaseModel;
|
||||||
use Utils\LogManager;
|
use Utils\LogManager;
|
||||||
|
|
||||||
if (!defined("AWS_REGION")) {
|
if (!defined("AWS_REGION")) {
|
||||||
@@ -79,6 +80,18 @@ if (defined('REDIS_SERVER_URI')
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$samlEnabled = SettingsManager::getInstance()->getSetting("SAML: Enabled");
|
||||||
|
if ($samlEnabled === '1') {
|
||||||
|
include APP_BASE_PATH . 'lib/saml2/Utilities.php';
|
||||||
|
include APP_BASE_PATH . 'lib/saml2/Response.php';
|
||||||
|
include APP_BASE_PATH . 'lib/saml2/encryption.php';
|
||||||
|
include APP_BASE_PATH . 'lib/saml2/mo-saml-options-enum.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
$instanceId = SettingsManager::getInstance()->getSetting("Instance : ID");
|
||||||
|
$instanceKey = SettingsManager::getInstance()->getSetting("Instance: Key");
|
||||||
|
if(!defined('APP_SEC')){define('APP_SEC',sha1($instanceId.$instanceKey));}
|
||||||
|
|
||||||
$noJSONRequests = SettingsManager::getInstance()->getSetting("System: Do not pass JSON in request");
|
$noJSONRequests = SettingsManager::getInstance()->getSetting("System: Do not pass JSON in request");
|
||||||
|
|
||||||
$debugMode = SettingsManager::getInstance()->getSetting("System: Debug Mode");
|
$debugMode = SettingsManager::getInstance()->getSetting("System: Debug Mode");
|
||||||
@@ -123,10 +136,12 @@ if (defined('CLIENT_PATH')) {
|
|||||||
|
|
||||||
$modelClassList = $moduleManagerObj->getModelClasses();
|
$modelClassList = $moduleManagerObj->getModelClasses();
|
||||||
$metaData = $moduleManagerObj->getModuleObject();
|
$metaData = $moduleManagerObj->getModuleObject();
|
||||||
|
/** @var BaseModel $modelClass */
|
||||||
foreach ($modelClassList as $modelClass) {
|
foreach ($modelClassList as $modelClass) {
|
||||||
$modelClassWithNameSpace = $metaData['model_namespace']."\\".$modelClass;
|
$modelClassWithNameSpace = $metaData['model_namespace']."\\".$modelClass;
|
||||||
$modelClassWithNameSpace::SetDatabaseAdapter($dbLocal);
|
$modelClassWithNameSpace::SetDatabaseAdapter($dbLocal);
|
||||||
$baseService->addModelClass($modelClass, $modelClassWithNameSpace);
|
$baseService->addModelClass($modelClass, $modelClassWithNameSpace);
|
||||||
|
$modelClassObject = new $modelClassWithNameSpace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,7 @@ class AttendanceActionManager extends SubActionManager
|
|||||||
//Find any open punch
|
//Find any open punch
|
||||||
$attendance = new Attendance();
|
$attendance = new Attendance();
|
||||||
$attendance->Load(
|
$attendance->Load(
|
||||||
"employee = ? and DATE_FORMAT( in_time, '%Y-%m-%d' ) = ? and (out_time is NULL
|
"employee = ? and DATE_FORMAT( in_time, '%Y-%m-%d' ) = ? and out_time is NULL",
|
||||||
or out_time = '0000-00-00 00:00:00')",
|
|
||||||
array($employee->id,$date)
|
array($employee->id,$date)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
namespace Classes;
|
namespace Classes;
|
||||||
|
|
||||||
|
use Model\BaseModel;
|
||||||
use Utils\LogManager;
|
use Utils\LogManager;
|
||||||
|
|
||||||
abstract class AbstractModuleManager
|
abstract class AbstractModuleManager
|
||||||
@@ -254,7 +255,17 @@ abstract class AbstractModuleManager
|
|||||||
protected function addModelClass($className)
|
protected function addModelClass($className)
|
||||||
{
|
{
|
||||||
$this->modelClasses[] = $className;
|
$this->modelClasses[] = $className;
|
||||||
BaseService::getInstance()->addModelClass($className, $this->moduleObject['model_namespace']."\\".$className);
|
$classWithNamespace = $this->moduleObject['model_namespace']."\\".$className;
|
||||||
|
BaseService::getInstance()->addModelClass($className, $classWithNamespace);
|
||||||
|
/** @var BaseModel $modelClass */
|
||||||
|
$modelClass = new $classWithNamespace();
|
||||||
|
if ($modelClass->isCustomFieldsEnabled()) {
|
||||||
|
$objectName = $modelClass->getObjectName();
|
||||||
|
BaseService::getInstance()->addCustomFieldClass(
|
||||||
|
$className,
|
||||||
|
(null === $objectName)? $className : $objectName
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addHistoryGeneric($type, $table, $refName, $refId, $field, $oldValue, $newValue)
|
protected function addHistoryGeneric($type, $table, $refName, $refId, $field, $oldValue, $newValue)
|
||||||
@@ -278,4 +289,12 @@ abstract class AbstractModuleManager
|
|||||||
{
|
{
|
||||||
BaseService::getInstance()->addCalculationHook($code, $name, $class, $method);
|
BaseService::getInstance()->addCalculationHook($code, $name, $class, $method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function install()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uninstall()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use Employees\Common\Model\EmployeeApproval;
|
|||||||
use FieldNames\Common\Model\CustomField;
|
use FieldNames\Common\Model\CustomField;
|
||||||
use FieldNames\Common\Model\FieldNameMapping;
|
use FieldNames\Common\Model\FieldNameMapping;
|
||||||
use Metadata\Common\Model\CalculationHook;
|
use Metadata\Common\Model\CalculationHook;
|
||||||
|
use Model\BaseModel;
|
||||||
use Model\DataEntryBackup;
|
use Model\DataEntryBackup;
|
||||||
use Model\Setting;
|
use Model\Setting;
|
||||||
use Modules\Common\Model\Module;
|
use Modules\Common\Model\Module;
|
||||||
@@ -51,7 +52,8 @@ class BaseService
|
|||||||
public $calculationHooks = array();
|
public $calculationHooks = array();
|
||||||
public $customFieldManager = null;
|
public $customFieldManager = null;
|
||||||
public $migrationManager = null;
|
public $migrationManager = null;
|
||||||
public $modelClassMap = array();
|
public $modelClassMap = [];
|
||||||
|
public $customFieldsClassMap = [];
|
||||||
public $currentProfileId = false;
|
public $currentProfileId = false;
|
||||||
|
|
||||||
protected $cacheService = null;
|
protected $cacheService = null;
|
||||||
@@ -167,6 +169,20 @@ class BaseService
|
|||||||
$this->modelClassMap[$modelClass] = $fullQualifiedName;
|
$this->modelClassMap[$modelClass] = $fullQualifiedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCustomFieldClassMap()
|
||||||
|
{
|
||||||
|
$map = [];
|
||||||
|
foreach ($this->customFieldsClassMap as $key => $val) {
|
||||||
|
$map[] = [$key, $val];
|
||||||
|
}
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCustomFieldClass($customFieldsClass, $objectName)
|
||||||
|
{
|
||||||
|
$this->customFieldsClassMap[$customFieldsClass] = $objectName;
|
||||||
|
}
|
||||||
|
|
||||||
public function getModelClassName($name)
|
public function getModelClassName($name)
|
||||||
{
|
{
|
||||||
return $this->getFullQualifiedModelClassName($name);
|
return $this->getFullQualifiedModelClassName($name);
|
||||||
@@ -637,7 +653,10 @@ class BaseService
|
|||||||
|
|
||||||
$processedList = array();
|
$processedList = array();
|
||||||
foreach ($list as $obj) {
|
foreach ($list as $obj) {
|
||||||
$processedList[] = $this->cleanUpAdoDB($obj->postProcessGetData($obj));
|
$processedObj = $this->cleanUpAdoDB($obj->postProcessGetData($obj));
|
||||||
|
if (null !== $processedObj) {
|
||||||
|
$processedList[] = $processedObj;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$list = $processedList;
|
$list = $processedList;
|
||||||
@@ -749,7 +768,7 @@ class BaseService
|
|||||||
$obj = $this->enrichObjectCustomFields($table, $obj);
|
$obj = $this->enrichObjectCustomFields($table, $obj);
|
||||||
|
|
||||||
$obj = $obj->postProcessGetElement($obj);
|
$obj = $obj->postProcessGetElement($obj);
|
||||||
return $this->cleanUpAdoDB($obj->postProcessGetData($obj));
|
return $this->cleanUpAdoDB($obj);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -928,6 +947,7 @@ class BaseService
|
|||||||
{
|
{
|
||||||
$fileFields = $this->fileFields;
|
$fileFields = $this->fileFields;
|
||||||
$nsTable = $this->getFullQualifiedModelClassName($table);
|
$nsTable = $this->getFullQualifiedModelClassName($table);
|
||||||
|
/** @var BaseModel $ele */
|
||||||
$ele = new $nsTable();
|
$ele = new $nsTable();
|
||||||
|
|
||||||
$ele->Load('id = ?', array($id));
|
$ele->Load('id = ?', array($id));
|
||||||
@@ -985,7 +1005,7 @@ class BaseService
|
|||||||
$dataEntryBackup->data = json_encode($newObj);
|
$dataEntryBackup->data = json_encode($newObj);
|
||||||
$dataEntryBackup->Save();
|
$dataEntryBackup->Save();
|
||||||
}
|
}
|
||||||
|
$ele->executePostDeleteActions($ele);
|
||||||
$this->audit(IceConstants::AUDIT_DELETE, "Deleted an object in ".$table." [id:".$ele->id."]");
|
$this->audit(IceConstants::AUDIT_DELETE, "Deleted an object in ".$table." [id:".$ele->id."]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,6 +1018,7 @@ class BaseService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$cfs = $this->customFieldManager->getCustomFields($table, $id);
|
$cfs = $this->customFieldManager->getCustomFields($table, $id);
|
||||||
|
/** @var CustomField $cf */
|
||||||
foreach ($cfs as $cf) {
|
foreach ($cfs as $cf) {
|
||||||
$cf->Delete();
|
$cf->Delete();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,6 +90,14 @@ class DomainAwareInputCleaner
|
|||||||
|
|
||||||
private function isValidFilterValue($input)
|
private function isValidFilterValue($input)
|
||||||
{
|
{
|
||||||
|
if (is_array($input)) {
|
||||||
|
$isValid = true;
|
||||||
|
foreach ($input as $val) {
|
||||||
|
$isValid = $isValid && !!preg_match('/^[-_: \d\p{L}]+$/u', $val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $isValid;
|
||||||
|
}
|
||||||
return !!preg_match('/^[-_: \d\p{L}]+$/u', $input);
|
return !!preg_match('/^[-_: \d\p{L}]+$/u', $input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
146
core/src/Classes/ExtensionManager.php
Normal file
146
core/src/Classes/ExtensionManager.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Classes;
|
||||||
|
|
||||||
|
use Utils\LogManager;
|
||||||
|
|
||||||
|
class ExtensionManager
|
||||||
|
{
|
||||||
|
const GROUP = 'extension';
|
||||||
|
protected function processExtensionInDB()
|
||||||
|
{
|
||||||
|
$dbModule = new \Modules\Common\Model\Module();
|
||||||
|
$extensions = $dbModule->Find("mod_group = ?", array(self::GROUP));
|
||||||
|
|
||||||
|
$extensionsInDB = [];
|
||||||
|
foreach ($extensions as $dbm) {
|
||||||
|
$extensionsInDB[$dbm->name] = $dbm;
|
||||||
|
ModuleAccessService::getInstance()->setModule($dbm->name, self::GROUP, $dbm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $extensionsInDB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionsPath()
|
||||||
|
{
|
||||||
|
return APP_BASE_PATH.'../extensions/';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExtensionMetaData($extensionName)
|
||||||
|
{
|
||||||
|
return json_decode(file_get_contents($this->getExtensionsPath().$extensionName.'/meta.json'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setupExtensions()
|
||||||
|
{
|
||||||
|
$menu = [];
|
||||||
|
$extensions = [];
|
||||||
|
$extensionDirs = scandir($this->getExtensionsPath());
|
||||||
|
$currentLocation = 0;
|
||||||
|
|
||||||
|
$extensionsInDB = $this->processExtensionInDB();
|
||||||
|
|
||||||
|
$needToInstall = false;
|
||||||
|
|
||||||
|
foreach ($extensionDirs as $extensionDir) {
|
||||||
|
if (is_dir($this->getExtensionsPath().$extensionDir) && $extensionDir != '.' && $extensionDir != '..') {
|
||||||
|
$meta = $this->getExtensionMetaData($extensionDir);
|
||||||
|
|
||||||
|
$arr = [];
|
||||||
|
$arr['name'] = $extensionDir;
|
||||||
|
$arr['label'] = $meta->label;
|
||||||
|
$arr['icon'] = $meta->icon;
|
||||||
|
$arr['menu'] = $meta->menu[0];
|
||||||
|
$arr['order'] = 0;
|
||||||
|
$arr['status'] = 'Enabled';
|
||||||
|
$arr['user_levels'] = $meta->user_levels;
|
||||||
|
$arr['user_roles'] = isset($meta->user_roles)?$meta->user_roles:"";
|
||||||
|
$arr['model_namespace'] = $meta->model_namespace;
|
||||||
|
$arr['manager'] = $meta->manager;
|
||||||
|
|
||||||
|
// Add menu
|
||||||
|
$menu[$meta->menu[0]] = $meta->menu[1];
|
||||||
|
|
||||||
|
//Check in admin dbmodules
|
||||||
|
if (isset($extensionsInDB[$arr['name']])) {
|
||||||
|
$dbModule = $extensionsInDB[$arr['name']];
|
||||||
|
|
||||||
|
$arr['name'] = $dbModule->name;
|
||||||
|
$arr['label'] = $dbModule->label;
|
||||||
|
$arr['icon'] = $dbModule->icon;
|
||||||
|
$arr['menu'] = $dbModule->menu;
|
||||||
|
$arr['status'] = $dbModule->status;
|
||||||
|
$arr['user_levels'] = json_decode($dbModule->user_levels);
|
||||||
|
$arr['user_roles'] = empty($dbModule->user_roles)
|
||||||
|
? [] : json_decode($dbModule->user_roles);
|
||||||
|
$arr['user_roles_blacklist'] = empty($dbModule->user_roles_blacklist)
|
||||||
|
? [] : json_decode($dbModule->user_roles_blacklist);
|
||||||
|
} else {
|
||||||
|
$dbModule = new \Modules\Common\Model\Module();
|
||||||
|
$dbModule->menu = $arr['menu'];
|
||||||
|
$dbModule->name = $arr['name'];
|
||||||
|
$dbModule->label = $arr['label'];
|
||||||
|
$dbModule->icon = $arr['icon'];
|
||||||
|
$dbModule->mod_group = self::GROUP;
|
||||||
|
$dbModule->mod_order = $arr['order'];
|
||||||
|
$dbModule->status = "Enabled";
|
||||||
|
$dbModule->version = isset($meta->version)?$meta->version:"";
|
||||||
|
$dbModule->update_path = self::GROUP.">".$extensionDir;
|
||||||
|
$dbModule->user_levels = isset($meta->user_levels)?json_encode($meta->user_levels):"";
|
||||||
|
$dbModule->user_roles = isset($meta->user_roles)?json_encode($meta->user_roles):"";
|
||||||
|
|
||||||
|
$ok = $dbModule->Save();
|
||||||
|
if (!$ok) {
|
||||||
|
LogManager::getInstance()->error('Error saving module: '.$dbModule->name);
|
||||||
|
}
|
||||||
|
$needToInstall = $ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @var \Classes\AbstractModuleManager */
|
||||||
|
$manager = $this->includeModuleManager($extensionDir, $arr);
|
||||||
|
if ($dbModule->status == 'Disabled') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($needToInstall) {
|
||||||
|
$manager->install();
|
||||||
|
}
|
||||||
|
|
||||||
|
$menuName = $arr['menu'];
|
||||||
|
if (!isset($extensions[$menuName])) {
|
||||||
|
$extensions[$menuName] = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$meta->headless) {
|
||||||
|
if ($arr['order'] == '0' || $arr['order'] == '') {
|
||||||
|
$extensions[$menuName]["Z".$currentLocation] = $arr;
|
||||||
|
$currentLocation++;
|
||||||
|
} else {
|
||||||
|
$extensions[$arr['menu']]["A".$arr['order']] = $arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$initializer = $manager->getInitializer();
|
||||||
|
if ($initializer !== null) {
|
||||||
|
$initializer->setBaseService(BaseService::getInstance());
|
||||||
|
$initializer->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$menu, $extensions];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function includeModuleManager($name, $data)
|
||||||
|
{
|
||||||
|
include($this->getExtensionsPath().$name.'/'.$name.'.php');
|
||||||
|
$moduleManagerClass = $data['manager'];
|
||||||
|
/* @var \Classes\AbstractModuleManager $moduleManagerObj*/
|
||||||
|
$moduleManagerObj = new $moduleManagerClass();
|
||||||
|
$moduleManagerObj->setModuleObject($data);
|
||||||
|
$moduleManagerObj->setModuleType(self::GROUP);
|
||||||
|
$moduleManagerObj->setModulePath(CLIENT_PATH.'/'.self::GROUP.'/'.$name);
|
||||||
|
\Classes\BaseService::getInstance()->addModuleManager($moduleManagerObj);
|
||||||
|
return $moduleManagerObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
core/src/Classes/IceExtension.php
Normal file
20
core/src/Classes/IceExtension.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
namespace Classes;
|
||||||
|
|
||||||
|
abstract class IceExtension extends AbstractModuleManager
|
||||||
|
{
|
||||||
|
public function initializeUserClasses()
|
||||||
|
{
|
||||||
|
// TODO: Implement initializeUserClasses() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializeFieldMappings()
|
||||||
|
{
|
||||||
|
// TODO: Implement initializeFieldMappings() method.
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializeDatabaseErrorMappings()
|
||||||
|
{
|
||||||
|
// TODO: Implement initializeDatabaseErrorMappings() method.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Classes;
|
namespace Classes;
|
||||||
|
|
||||||
use Firebase\JWT\JWT;
|
use Firebase\JWT\JWT;
|
||||||
|
use Firebase\JWT\SignatureInvalidException;
|
||||||
|
|
||||||
class JwtTokenService
|
class JwtTokenService
|
||||||
{
|
{
|
||||||
@@ -22,7 +23,11 @@ class JwtTokenService
|
|||||||
public function getBaseToken($jwtToken)
|
public function getBaseToken($jwtToken)
|
||||||
{
|
{
|
||||||
$secret = APP_SEC.APP_PASSWORD;
|
$secret = APP_SEC.APP_PASSWORD;
|
||||||
|
try {
|
||||||
$jwt = JWT::decode($jwtToken, $secret, array('HS256'));
|
$jwt = JWT::decode($jwtToken, $secret, array('HS256'));
|
||||||
|
} catch (SignatureInvalidException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (time() > intval($jwt->expire)) {
|
if (time() > intval($jwt->expire)) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class MemcacheService
|
|||||||
public static $openConnections = array();
|
public static $openConnections = array();
|
||||||
private static $me = null;
|
private static $me = null;
|
||||||
|
|
||||||
|
protected $inMemoryStore = [];
|
||||||
|
|
||||||
private function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -56,6 +58,15 @@ class MemcacheService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function set($key, $value, $expiry = 3600)
|
public function set($key, $value, $expiry = 3600)
|
||||||
|
{
|
||||||
|
if (!$this->setInServer($key, $value, $expiry)) {
|
||||||
|
$this->inMemoryStore[$this->compressKey($key)] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInServer($key, $value, $expiry = 3600)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\Memcached')) {
|
if (!class_exists('\\Memcached')) {
|
||||||
return false;
|
return false;
|
||||||
@@ -74,6 +85,19 @@ class MemcacheService
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function get($key)
|
public function get($key)
|
||||||
|
{
|
||||||
|
$data = $this->getFromServer($key);
|
||||||
|
if ($data) {
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
if (isset($this->inMemoryStore[$this->compressKey($key)])) {
|
||||||
|
return $this->inMemoryStore[$this->compressKey($key)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFromServer($key)
|
||||||
{
|
{
|
||||||
if (!class_exists('\\Memcached')) {
|
if (!class_exists('\\Memcached')) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
namespace Classes\Migration;
|
namespace Classes\Migration;
|
||||||
|
|
||||||
|
use Utils\LogManager;
|
||||||
|
|
||||||
abstract class AbstractMigration
|
abstract class AbstractMigration
|
||||||
{
|
{
|
||||||
protected $file;
|
protected $file;
|
||||||
@@ -16,7 +18,7 @@ abstract class AbstractMigration
|
|||||||
|
|
||||||
protected $lastError;
|
protected $lastError;
|
||||||
|
|
||||||
public function __construct($file)
|
public function __construct($file = null)
|
||||||
{
|
{
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
}
|
}
|
||||||
@@ -50,6 +52,7 @@ abstract class AbstractMigration
|
|||||||
$ret = $this->db()->Execute($sql);
|
$ret = $this->db()->Execute($sql);
|
||||||
if (!$ret) {
|
if (!$ret) {
|
||||||
$this->lastError = $this->db()->ErrorMsg();
|
$this->lastError = $this->db()->ErrorMsg();
|
||||||
|
LogManager::getInstance()->error('Error in migration: '.$this->lastError);
|
||||||
}
|
}
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class ModuleAccess
|
|||||||
* @param $name
|
* @param $name
|
||||||
* @param $group
|
* @param $group
|
||||||
*/
|
*/
|
||||||
public function __construct($name, $group)
|
public function __construct($name, $group = 'extension')
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->group = $group;
|
$this->group = $group;
|
||||||
|
|||||||
98
core/src/Classes/ModuleBuilderV2/ModuleBuilder.php
Normal file
98
core/src/Classes/ModuleBuilderV2/ModuleBuilder.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Thilina
|
||||||
|
* Date: 8/20/17
|
||||||
|
* Time: 9:47 AM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Classes\ModuleBuilderV2;
|
||||||
|
|
||||||
|
use Classes\PermissionManager;
|
||||||
|
|
||||||
|
class ModuleBuilder
|
||||||
|
{
|
||||||
|
public $modules = array();
|
||||||
|
public $user = null;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->user = \Classes\BaseService::getInstance()->getCurrentUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ModuleTab $module
|
||||||
|
*/
|
||||||
|
public function addModuleOrGroup($module)
|
||||||
|
{
|
||||||
|
$this->modules[] = $module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTabHeadersHTML()
|
||||||
|
{
|
||||||
|
$html = "";
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
$html .= $module->getHTML()."\r\n";
|
||||||
|
}
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTabPagesHTML()
|
||||||
|
{
|
||||||
|
$html = "";
|
||||||
|
/* @var ModuleTab $module */
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
if (get_class($module) === ModuleTab::class) {
|
||||||
|
$html .= $module->getPageHTML()."\r\n";
|
||||||
|
} else {
|
||||||
|
/* @var ModuleTab $mod */
|
||||||
|
foreach ($module->modules as $mod) {
|
||||||
|
$html .= $mod->getPageHTML()."\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getModJsHTML()
|
||||||
|
{
|
||||||
|
$moduleData = [
|
||||||
|
'user_level' => $this->user->user_level,
|
||||||
|
'permissions' => [
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
$html = "var modJsList = new Array();\r\n";
|
||||||
|
$activeModule = "";
|
||||||
|
/* @var ModuleTab $module */
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
if (get_class($module) == ModuleTab::class) {
|
||||||
|
$html .= $module->getJSObjectCode()."\r\n";
|
||||||
|
|
||||||
|
$modelClass = $module->modelPath;
|
||||||
|
$moduleData['permissions'][$module->name] = PermissionManager::checkGeneralAccess(new $modelClass());
|
||||||
|
|
||||||
|
if ($module->isActive) {
|
||||||
|
$activeModule = $module->name;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* @var ModuleTab $mod */
|
||||||
|
foreach ($module->modules as $mod) {
|
||||||
|
$modelClass = $mod->modelPath;
|
||||||
|
$moduleData['permissions'][$mod->name] = PermissionManager::checkGeneralAccess(new $modelClass());
|
||||||
|
|
||||||
|
if ($module->isActive && $activeModule == "") {
|
||||||
|
$activeModule = $mod->name;
|
||||||
|
}
|
||||||
|
$html .= $mod->getJSObjectCode()."\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "var modJs = modJsList['tab".$activeModule."'];\r\n";
|
||||||
|
|
||||||
|
$html = "var data = ".json_encode($moduleData).";\r\n".$html;
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
98
core/src/Classes/ModuleBuilderV2/ModuleTab.php
Normal file
98
core/src/Classes/ModuleBuilderV2/ModuleTab.php
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Thilina
|
||||||
|
* Date: 8/20/17
|
||||||
|
* Time: 9:47 AM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Classes\ModuleBuilderV2;
|
||||||
|
|
||||||
|
class ModuleTab
|
||||||
|
{
|
||||||
|
public $modelPath;
|
||||||
|
public $name;
|
||||||
|
public $class;
|
||||||
|
public $label;
|
||||||
|
public $adapterName;
|
||||||
|
public $filter;
|
||||||
|
public $orderBy;
|
||||||
|
public $isActive = false;
|
||||||
|
public $isInsideGroup = false;
|
||||||
|
public $options = array();
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
$modelPath,
|
||||||
|
$name,
|
||||||
|
$class,
|
||||||
|
$label,
|
||||||
|
$adapterName,
|
||||||
|
$filter,
|
||||||
|
$orderBy,
|
||||||
|
$isActive = false,
|
||||||
|
$options = array()
|
||||||
|
) {
|
||||||
|
|
||||||
|
$this->modelPath = $modelPath;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->class = $class;
|
||||||
|
$this->label = $label;
|
||||||
|
$this->adapterName = $adapterName;
|
||||||
|
$this->filter = $filter;
|
||||||
|
$this->orderBy = $orderBy;
|
||||||
|
$this->isActive = $isActive;
|
||||||
|
|
||||||
|
$this->options = array_merge(
|
||||||
|
$options,
|
||||||
|
[
|
||||||
|
"setObjectTypeName" => "'{$this->name}'",
|
||||||
|
"setAccess" => "data.permissions.{$this->name} ? data.permissions.{$this->name} : {}",
|
||||||
|
"setDataPipe" => 'new IceDataPipe(modJsList.tab' . $this->name . ')',
|
||||||
|
"setRemoteTable" => true,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHTML()
|
||||||
|
{
|
||||||
|
$active = ($this->isActive)?"active":"";
|
||||||
|
if (!$this->isInsideGroup) {
|
||||||
|
return '<li class="' . $active . '"><a id="tab' . $this->name
|
||||||
|
. '" href="#tabPage' . $this->name . '">' . t($this->label) . '</a></li>';
|
||||||
|
} else {
|
||||||
|
return '<li class="' . $active . '"><a id="tab' . $this->name
|
||||||
|
. '" href="#tabPage' . $this->name . '">' . t($this->label) . '</a></li>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPageHTML()
|
||||||
|
{
|
||||||
|
$active = ($this->isActive)?" active":"";
|
||||||
|
$html = '<div class="tab-pane'.$active.'" id="tabPage'.$this->name.'">'.
|
||||||
|
'<div id="'.$this->name.'Table" class="reviewBlock" data-content="List" style="padding-left:5px;"></div>'.
|
||||||
|
'<div id="'.$this->name.'Form"></div>'.
|
||||||
|
'<div id="'.$this->name.'FilterForm"></div>'.
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getJSObjectCode()
|
||||||
|
{
|
||||||
|
$js = "";
|
||||||
|
if (empty($this->filter)) {
|
||||||
|
$js.= "modJsList['tab" . $this->name . "'] = new " .
|
||||||
|
$this->adapterName . "('" . $this->class . "','" . $this->name . "','','".$this->orderBy. "');\r\n";
|
||||||
|
} else {
|
||||||
|
$js.= "modJsList['tab" . $this->name . "'] = new " .
|
||||||
|
$this->adapterName . "('" . $this->class . "','" . $this->name . "'," .
|
||||||
|
$this->filter . ",'".$this->orderBy. "');\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->options as $key => $val) {
|
||||||
|
$js.= "modJsList['tab" . $this->name . "'].".$key."(".$val. ");\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $js;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
core/src/Classes/ModuleBuilderV2/ModuleTabGroup.php
Normal file
53
core/src/Classes/ModuleBuilderV2/ModuleTabGroup.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: Thilina
|
||||||
|
* Date: 8/20/17
|
||||||
|
* Time: 9:48 AM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Classes\ModuleBuilderV2;
|
||||||
|
|
||||||
|
class ModuleTabGroup
|
||||||
|
{
|
||||||
|
public $name;
|
||||||
|
public $label;
|
||||||
|
public $isActive = false;
|
||||||
|
public $modules = array();
|
||||||
|
|
||||||
|
public function __construct($name, $label)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->label = $label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addModuleTab($moduleTab)
|
||||||
|
{
|
||||||
|
if ($moduleTab->isActive) {
|
||||||
|
$this->isActive = true;
|
||||||
|
$moduleTab->isActive = false;
|
||||||
|
}
|
||||||
|
$moduleTab->isInsideGroup = true;
|
||||||
|
$this->modules[] = $moduleTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHTML()
|
||||||
|
{
|
||||||
|
$html = "";
|
||||||
|
$active = ($this->isActive)?" active":"";
|
||||||
|
|
||||||
|
$html.= '<li class="dropdown'.$active.'">'."\r\n".
|
||||||
|
'<a href="#" id="'.$this->name.
|
||||||
|
'" class="dropdown-toggle" data-toggle="dropdown" aria-controls="'.$this->name.
|
||||||
|
'-contents">'.$this->label.' <span class="caret"></span></a>'."\r\n".
|
||||||
|
'<ul class="dropdown-menu" role="menu" aria-labelledby="'.$this->name.'" id="'.$this->name.'-contents">';
|
||||||
|
|
||||||
|
foreach ($this->modules as $module) {
|
||||||
|
$html.= $module->getHTML();
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .= "</ul></li>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,7 +103,7 @@ class PasswordManager
|
|||||||
return new IceResponse(IceResponse::ERROR, $error);
|
return new IceResponse(IceResponse::ERROR, $error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($password) > 20) {
|
if (strlen($password) > 30) {
|
||||||
$error = "Password too long";
|
$error = "Password too long";
|
||||||
|
|
||||||
return new IceResponse(IceResponse::ERROR, $error);
|
return new IceResponse(IceResponse::ERROR, $error);
|
||||||
|
|||||||
@@ -424,10 +424,8 @@ class RestEndPoint
|
|||||||
$token = $_GET['token'];
|
$token = $_GET['token'];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($token) > 32) {
|
|
||||||
$tokenService = new JwtTokenService();
|
$tokenService = new JwtTokenService();
|
||||||
$token = $tokenService->getBaseToken($token);
|
$token = $tokenService->getBaseToken($token);
|
||||||
}
|
|
||||||
|
|
||||||
return $token;
|
return $token;
|
||||||
}
|
}
|
||||||
|
|||||||
100
core/src/Classes/SAMLManager.php
Normal file
100
core/src/Classes/SAMLManager.php
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Classes;
|
||||||
|
|
||||||
|
use \RobRichards\XMLSecLibs\MoXMLSecurityKey;
|
||||||
|
use Utils\LogManager;
|
||||||
|
|
||||||
|
class SAMLManager
|
||||||
|
{
|
||||||
|
public function getSSOEmail($samlData, $relayState)
|
||||||
|
{
|
||||||
|
// Service Providers Assertion Consumer Service (ACS) URL
|
||||||
|
$acsUrl = CLIENT_BASE_URL.'login.php';
|
||||||
|
$samlResponse = htmlspecialchars($samlData);
|
||||||
|
|
||||||
|
$samlResponse = base64_decode($samlResponse);
|
||||||
|
|
||||||
|
$document = new \DOMDocument();
|
||||||
|
$document->loadXML($samlResponse);
|
||||||
|
$samlResponseXml = $document->firstChild;
|
||||||
|
|
||||||
|
$doc = $document->documentElement;
|
||||||
|
$xpath = new \DOMXpath($document);
|
||||||
|
$xpath->registerNamespace('samlp', 'urn:oasis:names:tc:SAML:2.0:protocol');
|
||||||
|
$xpath->registerNamespace('saml', 'urn:oasis:names:tc:SAML:2.0:assertion');
|
||||||
|
|
||||||
|
$status = $xpath->query('/samlp:Response/samlp:Status/samlp:StatusCode', $doc);
|
||||||
|
$statusString = $status->item(0)->getAttribute('Value');
|
||||||
|
|
||||||
|
|
||||||
|
$statusArray = explode(':', $statusString);
|
||||||
|
if (array_key_exists(7, $statusArray)) {
|
||||||
|
$status = $statusArray[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('Success' !== $status) {
|
||||||
|
$StatusMessage = $xpath->query('/samlp:Response/samlp:Status/samlp:StatusMessage', $doc)->item(0);
|
||||||
|
LogManager::getInstance()->error('SAML login failed: status = '. $status);
|
||||||
|
if (!empty($StatusMessage)) {
|
||||||
|
$StatusMessage = $StatusMessage->nodeValue;
|
||||||
|
LogManager::getInstance()->error('SAML login failed: status message = '. $StatusMessage);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$x509cert = SettingsManager::getInstance()->getSetting('SAML: X.509 Certificate');
|
||||||
|
|
||||||
|
$samlResponse = new \SAML2_Response($samlResponseXml);
|
||||||
|
$responseSignatureData = $samlResponse->getSignatureData();
|
||||||
|
$assertionSignatureData = current($samlResponse->getAssertions())->getSignatureData();
|
||||||
|
|
||||||
|
$certFingerPrint = MoXMLSecurityKey::getRawThumbprint($x509cert);
|
||||||
|
$certFingerPrint = preg_replace('/\s+/', '', $certFingerPrint);
|
||||||
|
$validSignature = false;
|
||||||
|
if (!empty($responseSignatureData)) {
|
||||||
|
$validSignature = \Utilities::processResponse(
|
||||||
|
$acsUrl,
|
||||||
|
$certFingerPrint,
|
||||||
|
$responseSignatureData,
|
||||||
|
$samlResponse,
|
||||||
|
0,
|
||||||
|
$relayState
|
||||||
|
);
|
||||||
|
LogManager::getInstance()->error('SAML: response signature validity :'.$validSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($assertionSignatureData)) {
|
||||||
|
$validSignature = \Utilities::processResponse(
|
||||||
|
$acsUrl,
|
||||||
|
$certFingerPrint,
|
||||||
|
$assertionSignatureData,
|
||||||
|
$samlResponse,
|
||||||
|
0,
|
||||||
|
$relayState
|
||||||
|
);
|
||||||
|
LogManager::getInstance()->error('SAML: response signature validity :'.$validSignature);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$validSignature) {
|
||||||
|
LogManager::getInstance()->error('Invalid response or assertion signature');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$issuer = current($samlResponse->getAssertions())->getIssuer();
|
||||||
|
$assertion = current($samlResponse->getAssertions());
|
||||||
|
$audiences = $assertion->getValidAudiences();
|
||||||
|
$expectedIssuer = SettingsManager::getInstance()->getSetting('SAML: IDP Issuer');
|
||||||
|
if ($issuer !== $expectedIssuer) {
|
||||||
|
LogManager::getInstance()->error('SAML Invalid Issuer :'.$issuer.' expected :'.$expectedIssuer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ssoEmail = current(current($samlResponse->getAssertions())->getNameId());
|
||||||
|
if (!$ssoEmail) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ssoEmail;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Classes;
|
namespace Classes;
|
||||||
|
|
||||||
|
use Classes\Crypt\AesCtr;
|
||||||
use Model\Setting;
|
use Model\Setting;
|
||||||
|
|
||||||
class SettingsManager
|
class SettingsManager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const ENCRYPTED_PREFIX = 'iceenc_';
|
||||||
|
|
||||||
private static $me = null;
|
private static $me = null;
|
||||||
|
|
||||||
|
private $encryptedSettings = [];
|
||||||
|
|
||||||
private function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
|
$this->addEncryptedSetting('SAML: X.509 Certificate');
|
||||||
|
$this->addEncryptedSetting('LDAP: Manager Password');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getInstance()
|
public static function getInstance()
|
||||||
@@ -21,9 +28,67 @@ class SettingsManager
|
|||||||
return self::$me;
|
return self::$me;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addEncryptedSetting($name)
|
||||||
|
{
|
||||||
|
if (!$this->isEncryptedSetting($name)) {
|
||||||
|
$this->encryptedSettings[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isEncryptedSetting($name)
|
||||||
|
{
|
||||||
|
return in_array($name, $this->encryptedSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInstanceKey()
|
||||||
|
{
|
||||||
|
$settings = new Setting();
|
||||||
|
$settings->Load("name = ?", array("Instance: Key"));
|
||||||
|
if ($settings->name != "Instance: Key") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $settings->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function encrypt($value)
|
||||||
|
{
|
||||||
|
$id = BaseService::getInstance()->getInstanceId();
|
||||||
|
$key = $this->getInstanceKey();
|
||||||
|
return AesCtr::encrypt($value, $id.$key, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function encryptSetting($name, $value)
|
||||||
|
{
|
||||||
|
// check the existence of prefix and encrypt only if need to avoid double encryption
|
||||||
|
if ($this->isEncryptedSetting($name)
|
||||||
|
&& substr($value, 0, strlen(self::ENCRYPTED_PREFIX)) !== self::ENCRYPTED_PREFIX
|
||||||
|
) {
|
||||||
|
$value = self::ENCRYPTED_PREFIX.$this->encrypt($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function decrypt($value)
|
||||||
|
{
|
||||||
|
$id = BaseService::getInstance()->getInstanceId();
|
||||||
|
$key = $this->getInstanceKey();
|
||||||
|
return AesCtr::decrypt($value, $id.$key, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decryptSetting($name, $value)
|
||||||
|
{
|
||||||
|
if ($this->isEncryptedSetting($name)
|
||||||
|
&& substr($value, 0, strlen(self::ENCRYPTED_PREFIX)) === self::ENCRYPTED_PREFIX
|
||||||
|
) {
|
||||||
|
$value = $this->decrypt(substr($value, strlen(self::ENCRYPTED_PREFIX)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
public function getSetting($name)
|
public function getSetting($name)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (class_exists("\\Classes\\ProVersion")) {
|
if (class_exists("\\Classes\\ProVersion")) {
|
||||||
$pro = new ProVersion();
|
$pro = new ProVersion();
|
||||||
$val = $pro->getSetting($name);
|
$val = $pro->getSetting($name);
|
||||||
@@ -34,20 +99,28 @@ class SettingsManager
|
|||||||
|
|
||||||
$setting = new Setting();
|
$setting = new Setting();
|
||||||
$setting->Load("name = ?", array($name));
|
$setting->Load("name = ?", array($name));
|
||||||
|
$value = null;
|
||||||
if ($setting->name == $name) {
|
if ($setting->name == $name) {
|
||||||
return $setting->value;
|
$value = $setting->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null === $value) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $this->decryptSetting($name, $value);
|
||||||
|
}
|
||||||
|
|
||||||
public function setSetting($name, $value)
|
public function setSetting($name, $value)
|
||||||
{
|
{
|
||||||
$setting = new Setting();
|
$setting = new Setting();
|
||||||
$setting->Load("name = ?", array($name));
|
$setting->Load("name = ?", array($name));
|
||||||
if ($setting->name == $name) {
|
if ($setting->name !== $name) {
|
||||||
$setting->value = $value;
|
return;
|
||||||
$setting->Save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$setting->value = $this->encryptSetting($name, $value);
|
||||||
|
$setting->Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addSetting($name, $value)
|
public function addSetting($name, $value)
|
||||||
@@ -55,14 +128,22 @@ class SettingsManager
|
|||||||
$setting = new Setting();
|
$setting = new Setting();
|
||||||
$setting->Load("name = ?", array($name));
|
$setting->Load("name = ?", array($name));
|
||||||
if ($setting->name == $name) {
|
if ($setting->name == $name) {
|
||||||
$setting->value = $value;
|
$setting->value = $this->encryptSetting($name, $value);
|
||||||
$setting->Save();
|
$setting->Save();
|
||||||
} else {
|
} else {
|
||||||
$setting->name = $name;
|
$setting->name = $name;
|
||||||
$setting->value = $value;
|
$setting->value = $this->encryptSetting($name, $value);
|
||||||
$setting->description = $value;
|
$setting->description = '';
|
||||||
$setting->meta = '';
|
$setting->meta = '';
|
||||||
$setting->Save();
|
$setting->Save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDeprecatedSettings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Attendance: Work Week Start Day',
|
||||||
|
'Attendance: Overtime Calculation Class'
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,14 @@ use Classes\IceResponse;
|
|||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
use Employees\Common\Model\Employee;
|
use Employees\Common\Model\Employee;
|
||||||
use Model\BaseModel;
|
use Model\BaseModel;
|
||||||
|
use Model\CustomFieldTrait;
|
||||||
|
|
||||||
class CompanyStructure extends BaseModel
|
class CompanyStructure extends BaseModel
|
||||||
{
|
{
|
||||||
|
use CustomFieldTrait;
|
||||||
|
public $objectName = 'Company Structures';
|
||||||
|
protected $allowCustomFields = true;
|
||||||
|
|
||||||
public $table = 'CompanyStructures';
|
public $table = 'CompanyStructures';
|
||||||
|
|
||||||
public function getAdminAccess()
|
public function getAdminAccess()
|
||||||
|
|||||||
25
core/src/CustomField/Admin/Api/CustomFieldAdminManager.php
Normal file
25
core/src/CustomField/Admin/Api/CustomFieldAdminManager.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace CustomField\Admin\Api;
|
||||||
|
|
||||||
|
use Classes\AbstractModuleManager;
|
||||||
|
|
||||||
|
class CustomFieldAdminManager extends AbstractModuleManager
|
||||||
|
{
|
||||||
|
public function initializeUserClasses()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializeFieldMappings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function initializeDatabaseErrorMappings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setupModuleClassDefinitions()
|
||||||
|
{
|
||||||
|
$this->addModelClass('CustomField');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,10 +45,12 @@ class DocumentTaskCreator implements TaskCreator
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = "select count(id) as c from EmployeeDocuments where employee = ? and valid_until < ?";
|
$query
|
||||||
|
= "select count(id) as c from EmployeeDocuments where employee = ? and valid_until < ? and visible_to = ?";
|
||||||
|
|
||||||
$user->DB()->SetFetchMode(ADODB_FETCH_ASSOC);
|
$user->DB()->SetFetchMode(ADODB_FETCH_ASSOC);
|
||||||
$rs = $user->DB()->Execute($query, [$employee->id, date('Y-m-d')]);
|
// TODO - sending notifications only for Owner documents, this need to be extended later
|
||||||
|
$rs = $user->DB()->Execute($query, [$employee->id, date('Y-m-d'), 'Owner']);
|
||||||
$count = $rs->fields['c'];
|
$count = $rs->fields['c'];
|
||||||
|
|
||||||
return $count;
|
return $count;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace Documents\Common\Model;
|
namespace Documents\Common\Model;
|
||||||
|
|
||||||
|
use Classes\BaseService;
|
||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
use Model\BaseModel;
|
use Model\BaseModel;
|
||||||
|
|
||||||
@@ -26,4 +27,23 @@ class Document extends BaseModel
|
|||||||
new ModuleAccess('documents', 'user'),
|
new ModuleAccess('documents', 'user'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function fieldValueMethods()
|
||||||
|
{
|
||||||
|
return ['getDocumentTypesForUser'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDocumentTypesForUser()
|
||||||
|
{
|
||||||
|
$documents = new Document();
|
||||||
|
if (BaseService::getInstance()->currentUser->user_level === 'Employee'
|
||||||
|
|| BaseService::getInstance()->currentUser->user_level === 'Restricted Employee'
|
||||||
|
) {
|
||||||
|
$documents = $documents->Find('share_with_employee = ?', ['Yes']);
|
||||||
|
} else {
|
||||||
|
$documents = $documents->Find('1 = 1');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $documents;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,65 @@
|
|||||||
|
|
||||||
namespace Documents\Common\Model;
|
namespace Documents\Common\Model;
|
||||||
|
|
||||||
|
use Classes\BaseService;
|
||||||
use Classes\IceResponse;
|
use Classes\IceResponse;
|
||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
|
use Employees\Common\Model\Employee;
|
||||||
use Model\BaseModel;
|
use Model\BaseModel;
|
||||||
|
|
||||||
class EmployeeDocument extends BaseModel
|
class EmployeeDocument extends BaseModel
|
||||||
{
|
{
|
||||||
public $table = 'EmployeeDocuments';
|
public $table = 'EmployeeDocuments';
|
||||||
|
|
||||||
|
private function getHiddenDocumentTypeIds()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// @codingStandardsIgnoreStart
|
||||||
|
public function Find($whereOrderBy, $bindarr = false, $cache = false, $pkeysArr = false, $extra = array())
|
||||||
|
{
|
||||||
|
$find = '';
|
||||||
|
$user = BaseService::getInstance()->getCurrentUser();
|
||||||
|
|
||||||
|
if ($user->user_level == 'Employee') {
|
||||||
|
$find = ' visible_to = \'Owner\' AND ';
|
||||||
|
$document = new Document();
|
||||||
|
$hiddenDocumentTypes = $document->Find(
|
||||||
|
"share_with_employee = ?",
|
||||||
|
['No']
|
||||||
|
);
|
||||||
|
|
||||||
|
$hiddenTypeIds = [];
|
||||||
|
foreach ($hiddenDocumentTypes as $hiddenDocumentType) {
|
||||||
|
$hiddenTypeIds[] = $hiddenDocumentType->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($hiddenTypeIds) > 0) {
|
||||||
|
$find .= ' document NOT IN (\''.implode('\',\'', $hiddenTypeIds).'\') AND ';
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::Find($find.$whereOrderBy, $bindarr, $pkeysArr, $extra);
|
||||||
|
|
||||||
|
} else if ($user->user_level == 'Manager') {
|
||||||
|
// Original $whereOrderBy already contain employee selection
|
||||||
|
// So here if isSubOrdinates is true if the query coming from Employee -> Document Management
|
||||||
|
// In that case we need to show documents from sub ordinates
|
||||||
|
// These docs can can be owner and manager both
|
||||||
|
if (isset($isSubOrdinates) && $isSubOrdinates) {
|
||||||
|
$find .= ' visible_to in (\'Owner\', \'Manager\') AND ';
|
||||||
|
} else {
|
||||||
|
// Here we are showing the documents for the manager
|
||||||
|
// If someone upload a document for this manager and make it visible to manager,
|
||||||
|
// that means only the manager of this manager can see the document
|
||||||
|
// So it should not be visible to this manager
|
||||||
|
$find .= ' visible_to in (\'Owner\') AND ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent::Find($find.$whereOrderBy, $bindarr, $pkeysArr, $extra);
|
||||||
|
}
|
||||||
|
// @codingStandardsIgnoreEnd
|
||||||
|
|
||||||
public function getAdminAccess()
|
public function getAdminAccess()
|
||||||
{
|
{
|
||||||
return array("get","element","save","delete");
|
return array("get","element","save","delete");
|
||||||
@@ -26,6 +77,8 @@ class EmployeeDocument extends BaseModel
|
|||||||
return array("get","element","save","delete");
|
return array("get","element","save","delete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function getUserAccess()
|
public function getUserAccess()
|
||||||
{
|
{
|
||||||
return array("get");
|
return array("get");
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Classes\ModuleAccess;
|
|||||||
use Company\Common\Model\CompanyStructure;
|
use Company\Common\Model\CompanyStructure;
|
||||||
use Metadata\Common\Model\Country;
|
use Metadata\Common\Model\Country;
|
||||||
use Model\BaseModel;
|
use Model\BaseModel;
|
||||||
|
use Model\CustomFieldTrait;
|
||||||
|
|
||||||
class Employee extends BaseModel
|
class Employee extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -90,6 +91,11 @@ class Employee extends BaseModel
|
|||||||
$this->oldObj = BaseService::getInstance()->getElement('Employee', $obj->id, $mapping, true);
|
$this->oldObj = BaseService::getInstance()->getElement('Employee', $obj->id, $mapping, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCustomFieldsEnabled()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private function saveHistory($obj)
|
private function saveHistory($obj)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use Classes\BaseService;
|
|||||||
use Classes\IceResponse;
|
use Classes\IceResponse;
|
||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
use Model\BaseModel;
|
use Model\BaseModel;
|
||||||
|
use Utils\LogManager;
|
||||||
|
|
||||||
class CustomField extends BaseModel
|
class CustomField extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -58,6 +59,22 @@ class CustomField extends BaseModel
|
|||||||
return new IceResponse(IceResponse::SUCCESS, "");
|
return new IceResponse(IceResponse::SUCCESS, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CustomField $obj
|
||||||
|
*/
|
||||||
|
public function executePostDeleteActions($obj)
|
||||||
|
{
|
||||||
|
$ret = $this->DB()->Execute(
|
||||||
|
'DELETE FROM CustomFieldValues where type = ? and name = ?',
|
||||||
|
[$obj->type, $obj->name]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!$ret) {
|
||||||
|
$this->lastError = $this->db()->ErrorMsg();
|
||||||
|
LogManager::getInstance()->error('Error deleting custom field values: '.$this->DB()->ErrorMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getModuleAccess()
|
public function getModuleAccess()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use Utils\LogManager;
|
|||||||
|
|
||||||
class BaseModel extends \ADOdb_Active_Record
|
class BaseModel extends \ADOdb_Active_Record
|
||||||
{
|
{
|
||||||
|
public $objectName = null;
|
||||||
|
protected $allowCustomFields = false;
|
||||||
|
|
||||||
public $keysToIgnore = array(
|
public $keysToIgnore = array(
|
||||||
"_table",
|
"_table",
|
||||||
@@ -195,6 +197,16 @@ class BaseModel extends \ADOdb_Active_Record
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function executePostDeleteActions($obj)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If null is returned the object wont be included in the response
|
||||||
|
*
|
||||||
|
* @param $obj
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
public function postProcessGetData($obj)
|
public function postProcessGetData($obj)
|
||||||
{
|
{
|
||||||
return $obj;
|
return $obj;
|
||||||
@@ -340,4 +352,14 @@ class BaseModel extends \ADOdb_Active_Record
|
|||||||
return $ok;
|
return $ok;
|
||||||
}
|
}
|
||||||
// @codingStandardsIgnoreEnd
|
// @codingStandardsIgnoreEnd
|
||||||
|
|
||||||
|
public function getObjectName()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCustomFieldsEnabled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
core/src/Model/CustomFieldTrait.php
Normal file
16
core/src/Model/CustomFieldTrait.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Model;
|
||||||
|
|
||||||
|
trait CustomFieldTrait
|
||||||
|
{
|
||||||
|
public function getObjectName()
|
||||||
|
{
|
||||||
|
return $this->objectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCustomFieldsEnabled()
|
||||||
|
{
|
||||||
|
return $this->allowCustomFields;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@ use Classes\BaseService;
|
|||||||
use Classes\IceResponse;
|
use Classes\IceResponse;
|
||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
use Classes\RestApiManager;
|
use Classes\RestApiManager;
|
||||||
|
use Classes\SettingsManager;
|
||||||
use Users\Common\Model\User;
|
use Users\Common\Model\User;
|
||||||
|
|
||||||
class Setting extends BaseModel
|
class Setting extends BaseModel
|
||||||
@@ -74,6 +75,12 @@ class Setting extends BaseModel
|
|||||||
return new IceResponse(IceResponse::SUCCESS, "");
|
return new IceResponse(IceResponse::SUCCESS, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function executePreSaveActions($obj)
|
||||||
|
{
|
||||||
|
$obj->value = SettingsManager::getInstance()->encryptSetting($obj->name, $obj->value);
|
||||||
|
return new IceResponse(IceResponse::SUCCESS, $obj);
|
||||||
|
}
|
||||||
|
|
||||||
public function executePreUpdateActions($obj)
|
public function executePreUpdateActions($obj)
|
||||||
{
|
{
|
||||||
return $this->executePreSaveActions($obj);
|
return $this->executePreSaveActions($obj);
|
||||||
@@ -87,5 +94,17 @@ class Setting extends BaseModel
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function postProcessGetData($obj)
|
||||||
|
{
|
||||||
|
if (in_array($obj->name, SettingsManager::getInstance()->getDeprecatedSettings())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($obj->value) > 30) {
|
||||||
|
$obj->value = substr($obj->value, 0, 30).'...';
|
||||||
|
}
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
public $table = 'Settings';
|
public $table = 'Settings';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
namespace Reports\Admin\Reports;
|
namespace Reports\Admin\Reports;
|
||||||
|
|
||||||
use Attendance\Common\Model\Attendance;
|
use Attendance\Common\Model\Attendance;
|
||||||
|
use Company\Common\Model\CompanyStructure;
|
||||||
use Employees\Common\Model\Employee;
|
use Employees\Common\Model\Employee;
|
||||||
use Reports\Admin\Api\ClassBasedReportBuilder;
|
use Reports\Admin\Api\ClassBasedReportBuilder;
|
||||||
use Reports\Admin\Api\ReportBuilderInterface;
|
use Reports\Admin\Api\ReportBuilderInterface;
|
||||||
@@ -16,6 +17,17 @@ class EmployeeTimeTrackReport extends ClassBasedReportBuilder implements ReportB
|
|||||||
LogManager::getInstance()->info(json_encode($report));
|
LogManager::getInstance()->info(json_encode($report));
|
||||||
LogManager::getInstance()->info(json_encode($req));
|
LogManager::getInstance()->info(json_encode($req));
|
||||||
|
|
||||||
|
if (empty($req['period'])
|
||||||
|
&& (
|
||||||
|
empty($req['date_start'])
|
||||||
|
|| 'NULL' === $req['date_start']
|
||||||
|
|| empty($req['date_end'])
|
||||||
|
|| 'NULL' === $req['date_end']
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$req['period'] = 'Current Month';
|
||||||
|
}
|
||||||
|
|
||||||
$employeeTimeEntry = new EmployeeTimeEntry();
|
$employeeTimeEntry = new EmployeeTimeEntry();
|
||||||
|
|
||||||
$timeEntryList = $employeeTimeEntry->Find(
|
$timeEntryList = $employeeTimeEntry->Find(
|
||||||
@@ -38,6 +50,8 @@ class EmployeeTimeTrackReport extends ClassBasedReportBuilder implements ReportB
|
|||||||
//$minutes = (int)($seconds/60);
|
//$minutes = (int)($seconds/60);
|
||||||
//Find Attendance Entries
|
//Find Attendance Entries
|
||||||
|
|
||||||
|
$req = $this->setRequestDatesBasedOnThePeriod($req);
|
||||||
|
|
||||||
$attendance = new Attendance();
|
$attendance = new Attendance();
|
||||||
$atteandanceList = $attendance->Find(
|
$atteandanceList = $attendance->Find(
|
||||||
"employee = ? and date(in_time) >= ? and date(out_time) <= ? and in_time < out_time",
|
"employee = ? and date(in_time) >= ? and date(out_time) <= ? and in_time < out_time",
|
||||||
@@ -66,21 +80,44 @@ class EmployeeTimeTrackReport extends ClassBasedReportBuilder implements ReportB
|
|||||||
$employeeObject = new Employee();
|
$employeeObject = new Employee();
|
||||||
$employeeObject->Load("id = ?", array($req['employee']));
|
$employeeObject->Load("id = ?", array($req['employee']));
|
||||||
|
|
||||||
|
$company = new CompanyStructure();
|
||||||
|
$company->Load('id = ?', [$employeeObject->department]);
|
||||||
|
|
||||||
$reportData = array();
|
$reportData = [];
|
||||||
//$reportData[] = array($employeeObject->first_name." ".$employeeObject->last_name,"","","","");
|
$reportData[] = [
|
||||||
$reportData[] = array("Date","First Punch-In Time","Last Punch-Out Time","Time in Office","Time in Timesheets");
|
"Date",
|
||||||
|
"First Punch-In Time",
|
||||||
|
"Last Punch-Out Time",
|
||||||
|
"Time in Attendance (Hours)",
|
||||||
|
"Time in Time-sheets (Hours)",
|
||||||
|
];
|
||||||
|
$reportData[] = ["Employee:",$employeeObject->first_name." ".$employeeObject->last_name,"","",""];
|
||||||
|
$reportData[] = ["Department:",$company->title,"","",""];
|
||||||
|
$reportData[] = ["Total Days:","","","",""];
|
||||||
|
|
||||||
|
|
||||||
//Iterate date range
|
//Iterate date range
|
||||||
|
|
||||||
$interval = \DateInterval::createFromDateString('1 day');
|
$interval = \DateInterval::createFromDateString('1 day');
|
||||||
$period = new \DatePeriod(new \DateTime($req['date_start']), $interval, new \DateTime($req['date_end']));
|
$period = new \DatePeriod(
|
||||||
|
new \DateTime($req['date_start']),
|
||||||
|
$interval,
|
||||||
|
(new \DateTime($req['date_end']))->modify('+1 day')
|
||||||
|
);
|
||||||
|
|
||||||
|
$totalHoursOffice = 0;
|
||||||
|
$totalHoursTimeSheets = 0;
|
||||||
|
$totalDaysForThePeriod = 0;
|
||||||
|
|
||||||
foreach ($period as $dt) {
|
foreach ($period as $dt) {
|
||||||
$dataRow = array();
|
$dataRow = array();
|
||||||
$key = $dt->format("Y-m-d");
|
$key = $dt->format("Y-m-d");
|
||||||
|
|
||||||
|
if (!isset($firstTimeInArray[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalDaysForThePeriod++;
|
||||||
$dataRow[] = $key;
|
$dataRow[] = $key;
|
||||||
|
|
||||||
if (isset($firstTimeInArray[$key])) {
|
if (isset($firstTimeInArray[$key])) {
|
||||||
@@ -107,8 +144,45 @@ class EmployeeTimeTrackReport extends ClassBasedReportBuilder implements ReportB
|
|||||||
$dataRow[] = 0;
|
$dataRow[] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$totalHoursOffice += $dataRow[3];
|
||||||
|
$totalHoursTimeSheets += $dataRow[4];
|
||||||
|
|
||||||
|
$dataRow[3] = number_format($dataRow[3], 2, '.', '');
|
||||||
|
$dataRow[4] = number_format($dataRow[4], 2, '.', '');
|
||||||
|
|
||||||
$reportData[] = $dataRow;
|
$reportData[] = $dataRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$reportData[3][1] = $totalDaysForThePeriod;
|
||||||
|
|
||||||
|
$totalHoursOffice = number_format($totalHoursOffice, 2, '.', '');
|
||||||
|
$totalHoursTimeSheets = number_format($totalHoursTimeSheets, 2, '.', '');
|
||||||
|
|
||||||
|
$reportData[] = ["Total","","",$totalHoursOffice,$totalHoursTimeSheets];
|
||||||
|
|
||||||
return $reportData;
|
return $reportData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function setRequestDatesBasedOnThePeriod($req)
|
||||||
|
{
|
||||||
|
if (empty($req['period'])) {
|
||||||
|
return $req;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($req['period'] === 'Current Month') {
|
||||||
|
$req['date_start'] = date('Y-m-01', strtotime('now'));
|
||||||
|
$req['date_end'] = date('Y-m-d', strtotime('now'));
|
||||||
|
} elseif ($req['period'] === 'Last Month') {
|
||||||
|
$req['date_start'] = date('Y-m-d', strtotime('first day of last month'));
|
||||||
|
$req['date_end'] = date('Y-m-d', strtotime('last day of last month'));
|
||||||
|
} elseif ($req['period'] === 'Last Week') {
|
||||||
|
$req['date_start'] = date("Y-m-d", strtotime("-7 days"));
|
||||||
|
$req['date_end'] = date('Y-m-d', strtotime('now'));
|
||||||
|
} elseif ($req['period'] === 'Last 2 Weeks') {
|
||||||
|
$req['date_start'] = date("Y-m-d", strtotime("-14 days"));
|
||||||
|
$req['date_end'] = date('Y-m-d', strtotime('now'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $req;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ class StaffDirectory extends Employee
|
|||||||
foreach ($res as $entry) {
|
foreach ($res as $entry) {
|
||||||
$emp = new BaseModel();
|
$emp = new BaseModel();
|
||||||
$emp->id = $entry->id;
|
$emp->id = $entry->id;
|
||||||
$emp = FileService::getInstance()->updateProfileImage($emp);
|
|
||||||
//$emp->image = str_replace("_img_",$emp->image,$img);
|
|
||||||
$emp->first_name = $entry->first_name;
|
$emp->first_name = $entry->first_name;
|
||||||
$emp->last_name = $entry->last_name;
|
$emp->last_name = $entry->last_name;
|
||||||
|
$emp = FileService::getInstance()->updateSmallProfileImage($emp);
|
||||||
$emp->job_title = $entry->job_title;
|
$emp->job_title = $entry->job_title;
|
||||||
$emp->department = $entry->department;
|
$emp->department = $entry->department;
|
||||||
$emp->work_phone = $entry->work_phone;
|
$emp->work_phone = $entry->work_phone;
|
||||||
@@ -41,6 +40,11 @@ class StaffDirectory extends Employee
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isCustomFieldsEnabled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// @codingStandardsIgnoreStart
|
// @codingStandardsIgnoreStart
|
||||||
public function Insert()
|
public function Insert()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ namespace Travel\Common\Model;
|
|||||||
use Classes\ModuleAccess;
|
use Classes\ModuleAccess;
|
||||||
use Classes\SettingsManager;
|
use Classes\SettingsManager;
|
||||||
use Model\ApproveModel;
|
use Model\ApproveModel;
|
||||||
|
use Model\CustomFieldTrait;
|
||||||
|
|
||||||
class EmployeeTravelRecord extends ApproveModel
|
class EmployeeTravelRecord extends ApproveModel
|
||||||
{
|
{
|
||||||
|
use CustomFieldTrait;
|
||||||
|
|
||||||
public $table = 'EmployeeTravelRecords';
|
public $table = 'EmployeeTravelRecords';
|
||||||
|
public $objectName = 'Travel Request';
|
||||||
|
protected $allowCustomFields = true;
|
||||||
|
|
||||||
public $notificationModuleName = "Travel Management";
|
public $notificationModuleName = "Travel Management";
|
||||||
public $notificationUnitName = "TravelRequest";
|
public $notificationUnitName = "TravelRequest";
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ namespace Travel\Common\Model;
|
|||||||
|
|
||||||
class EmployeeTravelRecordApproval extends EmployeeTravelRecord
|
class EmployeeTravelRecordApproval extends EmployeeTravelRecord
|
||||||
{
|
{
|
||||||
|
protected $allowCustomFields = false;
|
||||||
|
|
||||||
// @codingStandardsIgnoreStart
|
// @codingStandardsIgnoreStart
|
||||||
public function Find($whereOrderBy, $bindarr = false, $cache = false, $pkeysArr = false, $extra = array())
|
public function Find($whereOrderBy, $bindarr = false, $cache = false, $pkeysArr = false, $extra = array())
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ gulp.task('admin-js', (done) => {
|
|||||||
const files = [
|
const files = [
|
||||||
'attendance',
|
'attendance',
|
||||||
'company_structure',
|
'company_structure',
|
||||||
|
'custom_fields',
|
||||||
'clients',
|
'clients',
|
||||||
'charts',
|
'charts',
|
||||||
'dashboard',
|
'dashboard',
|
||||||
|
|||||||
339
release.md
339
release.md
@@ -1,3 +1,41 @@
|
|||||||
|
# Release Notes IceHrm Open Source
|
||||||
|
|
||||||
|
## Release note v29.0.0.OS
|
||||||
|
|
||||||
|
### 🧲 New features
|
||||||
|
|
||||||
|
* SAML support [https://icehrm.gitbook.io/icehrm/api-and-single-sign-on/sign-in-with-saml-okta](https://icehrm.gitbook.io/icehrm/api-and-single-sign-on/sign-in-with-saml-okta)
|
||||||
|
* Ability to control who can see employee documents
|
||||||
|
* New custom field module. With this module users can manage all the custom fields via a single module.
|
||||||
|
* Adding custom field support for company structure.
|
||||||
|
* UI improvements for client module.
|
||||||
|
* Introducing encrypted settings.
|
||||||
|
* Adding additional fields such as total time to employee time tracking report.
|
||||||
|
* Improvements to icehrm custom extension development [https://icehrm.gitbook.io/icehrm/developer-guide/creating-first-extension](https://icehrm.gitbook.io/icehrm/developer-guide/creating-first-extension)
|
||||||
|
|
||||||
|
### 🛡️ Security improvements
|
||||||
|
|
||||||
|
* More restrictive criteria for user passwords.
|
||||||
|
* Removing support for legacy API tokens. (if you are using the mobile app your users will need to re authorize)
|
||||||
|
* Removing unused custom field values.
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
* Only relevant settings are displayed, under the `Other` tab on settings module
|
||||||
|
|
||||||
|
## Release note v28.2.0.OS
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
* 🦠 💉 Custom extensions [https://icehrm.gitbook.io/icehrm/developer-guide/creating-first-extension](https://icehrm.gitbook.io/icehrm/developer-guide/creating-first-extension)
|
||||||
|
|
||||||
|
## Release note v28.1.1.OS
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* Fixing inability to filter employee documents
|
||||||
|
* Fixed the issue with selecting projects when adding timesheets details
|
||||||
|
* Fix issues occurred due to incorrectly configured API
|
||||||
|
|
||||||
## Release note v28.1.0.OS
|
## Release note v28.1.0.OS
|
||||||
|
|
||||||
### 🧲 New features
|
### 🧲 New features
|
||||||
@@ -16,81 +54,97 @@
|
|||||||
* New REST endpoints for employee qualifications
|
* New REST endpoints for employee qualifications
|
||||||
|
|
||||||
### 🐛 Bug fixes
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
* Fixed, issue with managers being able to create performance reviews for employees who are not their direct reports
|
* Fixed, issue with managers being able to create performance reviews for employees who are not their direct reports
|
||||||
* Fixed, issues related to using full profile image instead of using smaller version of profile image
|
* Fixed, issues related to using full profile image instead of using smaller version of profile image
|
||||||
* Changing third gender to other
|
* Changing third gender to other
|
||||||
* Improvements and fixes for internal frontend data caching
|
* Improvements and fixes for internal frontend data caching
|
||||||
|
|
||||||
|
## Release note v27.0.2.OS
|
||||||
|
|
||||||
|
This fixes some major issues found in v27.0.1.OS
|
||||||
|
|
||||||
|
### 🐛 Bug fixes
|
||||||
|
|
||||||
|
* Filtering across whole application was broken and now it's fixed
|
||||||
|
* Fixed the issue related to photo not being shown to the admin when photo attendance is enabled
|
||||||
|
|
||||||
|
### 🧑🏻💻 For developers
|
||||||
|
|
||||||
|
* We have added support for vagrant development environment based on Debian 10 / PHP 7.3 \(with Xdebug\) / Nginx / MySQL
|
||||||
|
|
||||||
## Release note v27.0.0.OS
|
## Release note v27.0.0.OS
|
||||||
|
|
||||||
### New features
|
### New features
|
||||||
|
|
||||||
* Employee document management is now available for open-source version
|
* Employee document management is now available for open-source version
|
||||||
* UI/UX improvements (new fonts / better spacing)
|
* UI/UX improvements \(new fonts / better spacing\)
|
||||||
* Payroll module improvements
|
* Payroll module improvements
|
||||||
* Security improvements to password policy
|
* Security improvements to password policy
|
||||||
* Albanian language is now available
|
* Albanian language is now available
|
||||||
* Ability to deploy using docker
|
* Ability to deploy using docker
|
||||||
|
|
||||||
### For developers
|
### For developers
|
||||||
* Developer environment based on docker [https://www.youtube.com/watch?v=sz8OV_ON6S8](https://www.youtube.com/watch?v=sz8OV_ON6S8)
|
|
||||||
|
* Developer environment based on docker [https://www.youtube.com/watch?v=sz8OV\_ON6S8](https://www.youtube.com/watch?v=sz8OV_ON6S8)
|
||||||
* [Developer guide](https://icehrm.gitbook.io/icehrm/developer-guide/create-new-module)
|
* [Developer guide](https://icehrm.gitbook.io/icehrm/developer-guide/create-new-module)
|
||||||
* Fully supports all php versions >= 5.6 upto v7.3 (php 5.6 support is deprecated and not recommended)
|
* Fully supports all php versions >= 5.6 upto v7.3 \(php 5.6 support is deprecated and not recommended\)
|
||||||
|
|
||||||
### Bug fixes
|
### Bug fixes
|
||||||
* Fixes to newly found vulnerabilities (https://github.com/gamonoid/icehrm/issues/213): credits to: [Talos](https://talosintelligence.com/)
|
|
||||||
|
* Fixes to newly found vulnerabilities \([https://github.com/gamonoid/icehrm/issues/213](https://github.com/gamonoid/icehrm/issues/213)\): credits to: [Talos](https://talosintelligence.com/)
|
||||||
* Fixed the travel request approval for managers
|
* Fixed the travel request approval for managers
|
||||||
* Fixed the issue with attendance source IP display
|
* Fixed the issue with attendance source IP display
|
||||||
* Fixing Api issues in PHP 7.3
|
* Fixing Api issues in PHP 7.3
|
||||||
|
|
||||||
|
## Release note v26.6.0.OS
|
||||||
Release note v26.6.0.OS
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Some Improvements to UI such as updating Icons and upgrading font-awesome to its latest version
|
* Some Improvements to UI such as updating Icons and upgrading font-awesome to its latest version
|
||||||
* Tracking IP and location of the employee when marking attendance, this is done when updating attendance via mobile
|
* Tracking IP and location of the employee when marking attendance, this is done when updating attendance via mobile
|
||||||
* Ability to control location tracking via mobile using server side settings
|
* Ability to control location tracking via mobile using server side settings
|
||||||
* Improvements to translations
|
* Improvements to translations
|
||||||
* Compatible with location tracking with icehrm mobile app
|
|
||||||
|
|
||||||
### Mobile App
|
### Mobile App
|
||||||
* This release is coupled with mobile application release on AppStore (https://apple.co/2Yrtxoy) and Google Play (http://bit.ly/2OkMmKe)
|
|
||||||
|
* This release is coupled with mobile application release on AppStore \([https://apple.co/2Yrtxoy](https://apple.co/2Yrtxoy)\) and Google Play \([http://bit.ly/2OkMmKe](http://bit.ly/2OkMmKe)\)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* Order projects by name on Timesheet project listing (This is to make it easier to edit timesheets with many projects)
|
|
||||||
|
* Order projects by name on Timesheet project listing \(This is to make it easier to edit timesheets with many projects\)
|
||||||
* Link home page user profile to employee profile update page
|
* Link home page user profile to employee profile update page
|
||||||
* Fix issues related to configuring Api with mobile app
|
* Fix issues related to configuring Api with mobile app
|
||||||
|
|
||||||
### Security Improvements
|
### Security Improvements
|
||||||
|
|
||||||
* Upgrade npm missing dependencies
|
* Upgrade npm missing dependencies
|
||||||
|
|
||||||
|
## Release note v26.2.0.OS
|
||||||
Release note v26.2.0.OS
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add staff directory module
|
* Add staff directory module
|
||||||
* Update client-side js to ES6
|
* Update client-side js to ES6
|
||||||
* Compatible with IceHrm Mobile App
|
* Compatible with IceHrm Mobile App
|
||||||
* Use npm libraries when possible
|
* Use npm libraries when possible
|
||||||
* Add gulp build for frontend assets
|
* Add gulp build for frontend assets
|
||||||
* Allow generating QR code with rest api key (https://github.com/gamonoid/icehrm/issues/169)
|
* Allow generating QR code with rest api key \([https://github.com/gamonoid/icehrm/issues/169](https://github.com/gamonoid/icehrm/issues/169)\)
|
||||||
* Updated readme for development setup with vagrant
|
* Updated readme for development setup with vagrant
|
||||||
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Add missing employee details report
|
* Add missing employee details report
|
||||||
* Fix: Labels of 'Employee Custom Fields' not displayed (https://github.com/gamonoid/icehrm/issues/146)
|
* Fix: Labels of 'Employee Custom Fields' not displayed \([https://github.com/gamonoid/icehrm/issues/146](https://github.com/gamonoid/icehrm/issues/146)\)
|
||||||
* Fix: Work week for all counties can not be edited
|
* Fix: Work week for all counties can not be edited
|
||||||
* Fix: Custom fields are not shown under employee profile (https://github.com/gamonoid/icehrm/issues/159)
|
* Fix: Custom fields are not shown under employee profile \([https://github.com/gamonoid/icehrm/issues/159](https://github.com/gamonoid/icehrm/issues/159)\)
|
||||||
* Fix: Additional buttons shown below timesheet list (https://github.com/gamonoid/icehrm/issues/171)
|
* Fix: Additional buttons shown below timesheet list \([https://github.com/gamonoid/icehrm/issues/171](https://github.com/gamonoid/icehrm/issues/171)\)
|
||||||
* Updates to Italian translations (https://github.com/gamonoid/icehrm/pull/166) by https://github.com/nightwatch75
|
* Updates to Italian translations \([https://github.com/gamonoid/icehrm/pull/166](https://github.com/gamonoid/icehrm/pull/166)\) by [https://github.com/nightwatch75](https://github.com/nightwatch75)
|
||||||
|
|
||||||
|
## Release note v24.0.0.OS
|
||||||
Release note v24.0.0.OS
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Allow passing additional parameters to payroll predefined methods
|
* Allow passing additional parameters to payroll predefined methods
|
||||||
* Pass leave type name in function field to get leave count for a given type
|
* Pass leave type name in function field to get leave count for a given type
|
||||||
* Add employee name to payroll report
|
* Add employee name to payroll report
|
||||||
@@ -99,31 +153,35 @@ Release note v24.0.0.OS
|
|||||||
* Add filter by status feature to subordinate time sheets
|
* Add filter by status feature to subordinate time sheets
|
||||||
|
|
||||||
### Security Fixes
|
### Security Fixes
|
||||||
|
|
||||||
* Fix missing login form CSRF token
|
* Fix missing login form CSRF token
|
||||||
* Fix risky usage of hashed password in request
|
* Fix risky usage of hashed password in request
|
||||||
* Fixing permission issues on module access for each user level
|
* Fixing permission issues on module access for each user level
|
||||||
* Prevent manager from accessing sensitive user records
|
* Prevent manager from accessing sensitive user records
|
||||||
|
|
||||||
### Other Fixes
|
### Other Fixes
|
||||||
|
|
||||||
* Hide employee salary from managers
|
* Hide employee salary from managers
|
||||||
* Prevent manager from accessing audit, cron and notifications
|
* Prevent manager from accessing audit, cron and notifications
|
||||||
* Prevent managers from deleting employees
|
* Prevent managers from deleting employees
|
||||||
* Validate overtime start and end times
|
* Validate overtime start and end times
|
||||||
* Fix issue: employee can download draft payroll
|
* Fix issue: employee can download draft payroll
|
||||||
|
|
||||||
Release note v23.0.1.OS
|
## Release note v23.0.1.OS
|
||||||
------------------------
|
|
||||||
This release include some very critical security fixes. We recommend upgrading your installation to latest release.
|
This release include some very critical security fixes. We recommend upgrading your installation to latest release.
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix missing login form CSRF token
|
* Fix missing login form CSRF token
|
||||||
* Fix risky usage of hashed password in request
|
* Fix risky usage of hashed password in request
|
||||||
|
|
||||||
Release note v23.0.0.OS
|
## Release note v23.0.0.OS
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Loading last used module when revisiting application
|
* Loading last used module when revisiting application
|
||||||
* Finnish language support (Beta)
|
* Finnish language support \(Beta\)
|
||||||
* Improvements to German, Italian and Chinese language translations
|
* Improvements to German, Italian and Chinese language translations
|
||||||
* Allow quickly switching languages
|
* Allow quickly switching languages
|
||||||
* Improvements to security for preventing possible LFI attacks
|
* Improvements to security for preventing possible LFI attacks
|
||||||
@@ -132,106 +190,122 @@ Release note v23.0.0.OS
|
|||||||
* Allow importing approved overtime hours into payroll
|
* Allow importing approved overtime hours into payroll
|
||||||
* Add date and time masks
|
* Add date and time masks
|
||||||
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix logout cookie issue, by clearing remember me cookie when logging out
|
* Fix logout cookie issue, by clearing remember me cookie when logging out
|
||||||
* Improve privacy for GDPR
|
* Improve privacy for GDPR
|
||||||
* Improvements to file upload field
|
* Improvements to file upload field
|
||||||
* Fix issue: attendance rest end point not working on php 5.6
|
* Fix issue: attendance rest end point not working on php 5.6
|
||||||
|
|
||||||
Release note v22.0.0.OS
|
## Release note v22.0.0.OS
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Improvements to module naming
|
* Improvements to module naming
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix issue: filter dialog default values are not selected
|
* Fix issue: filter dialog default values are not selected
|
||||||
* Fix issue: department head can be an employee outside the department
|
* Fix issue: department head can be an employee outside the department
|
||||||
* Fix issue: department head or supervisor (who has manager leave access) can't use switch employee feature
|
* Fix issue: department head or supervisor \(who has manager leave access\) can't use switch employee feature
|
||||||
* Fix issue: employee name is not visible on report if middle name is empty
|
* Fix issue: employee name is not visible on report if middle name is empty
|
||||||
|
|
||||||
Release note v21.1.0.OS
|
## Release note v21.1.0.OS
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
* UI improvements (help button and error messages)
|
|
||||||
|
* UI improvements \(help button and error messages\)
|
||||||
* Allow adding placeholders to text fields
|
* Allow adding placeholders to text fields
|
||||||
* Improvements to German Translations
|
* Improvements to German Translations
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fixing notification issues
|
* Fixing notification issues
|
||||||
|
|
||||||
Release note v21.0.0.OS
|
## Release note v21.0.0.OS
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Fully compatible with php 7.1
|
* Fully compatible with php 7.1
|
||||||
* Add Net_SMTP via composer (no pear installation needed)
|
* Add Net\_SMTP via composer \(no pear installation needed\)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fixes for web servers not supporting JSON in GET request
|
* Fixes for web servers not supporting JSON in GET request
|
||||||
|
|
||||||
Release note v21.0.0.OS
|
## Release note v21.0.0.OS
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Fully compatible with php 7.1
|
* Fully compatible with php 7.1
|
||||||
* Add Net_SMTP via composer (no pear installation needed)
|
* Add Net\_SMTP via composer \(no pear installation needed\)
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fixes for web servers not supporting JSON in GET request
|
* Fixes for web servers not supporting JSON in GET request
|
||||||
|
|
||||||
Release note v20.3.0.PRO
|
## Release note v20.3.0.PRO
|
||||||
------------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Employee and Attendance REST Api Released
|
* Employee and Attendance REST Api Released
|
||||||
* Import/Export for Payroll Configurations
|
* Import/Export for Payroll Configurations
|
||||||
* Ability to import employee approved time sheet hours to payroll
|
* Ability to import employee approved time sheet hours to payroll
|
||||||
* Swift Mailer based SMTP support (no need to install Net_SMTP anymore)
|
* Swift Mailer based SMTP support \(no need to install Net\_SMTP anymore\)
|
||||||
* Add direct Edit button on employee list
|
* Add direct Edit button on employee list
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix DB connection issues due to special characters in password
|
* Fix DB connection issues due to special characters in password
|
||||||
* Fixes for custom field saving issues in mysql v5.7.x
|
* Fixes for custom field saving issues in mysql v5.7.x
|
||||||
|
|
||||||
Release note v20.2
|
## Release note v20.2
|
||||||
------------------
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix for resetting modules
|
* Fix for resetting modules
|
||||||
|
|
||||||
Release note v20.1
|
## Release note v20.1
|
||||||
------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Compatible with MySQL 5.7 Strict Mode
|
* Compatible with MySQL 5.7 Strict Mode
|
||||||
* PSR-2 compatible code
|
* PSR-2 compatible code
|
||||||
* Employee History Module
|
* Employee History Module
|
||||||
* Staff Directory
|
* Staff Directory
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix: password reset not working
|
* Fix: password reset not working
|
||||||
* Fix: limiting selectable countries via Settings
|
* Fix: limiting selectable countries via Settings
|
||||||
* Fix for resetting modules
|
* Fix for resetting modules
|
||||||
|
|
||||||
Release note v20.0
|
## Release note v20.0
|
||||||
------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Payroll Module
|
* Payroll Module
|
||||||
* Compatible with MySQL 5.7 Strict Mode
|
* Compatible with MySQL 5.7 Strict Mode
|
||||||
* Namespaced Classes
|
* Namespaced Classes
|
||||||
* LDAP Module
|
* LDAP Module
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix: limiting selectable countries via Settings
|
* Fix: limiting selectable countries via Settings
|
||||||
|
|
||||||
Release note v19.0
|
## Release note v19.0
|
||||||
------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Development environment
|
* Development environment
|
||||||
* Overtime module
|
* Overtime module
|
||||||
* Department heads who can manage all employees attached to a company structure
|
* Department heads who can manage all employees attached to a company structure
|
||||||
|
|
||||||
|
## Release note v18.0
|
||||||
|
|
||||||
Release note v18.0
|
|
||||||
------------------
|
|
||||||
### Features
|
### Features
|
||||||
* Translations (beta) for German, French, Polish, Italian, Sinhala, Chinese, Japanese, Hindi and Spanish
|
|
||||||
|
* Translations \(beta\) for German, French, Polish, Italian, Sinhala, Chinese, Japanese, Hindi and Spanish
|
||||||
* PDF Reports
|
* PDF Reports
|
||||||
* Ability to specify department heads
|
* Ability to specify department heads
|
||||||
* Add advanced custom fields to employees via UI
|
* Add advanced custom fields to employees via UI
|
||||||
@@ -245,19 +319,20 @@ Release note v18.0
|
|||||||
* Report files module - Allow downloading all previously generated reports
|
* Report files module - Allow downloading all previously generated reports
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix: subordinates are not showing beyond first page issue.
|
* Fix: subordinates are not showing beyond first page issue.
|
||||||
|
|
||||||
|
## Release note v16.1
|
||||||
Release note v16.1
|
|
||||||
------------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix LDAP user login issue
|
* Fix LDAP user login issue
|
||||||
* Allow creating users with username having dot and dash
|
* Allow creating users with username having dot and dash
|
||||||
|
|
||||||
Release note v16.0
|
## Release note v16.0
|
||||||
------------------
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Advanced Employee Management Module is now included in IceHrm Open Source Edition
|
* Advanced Employee Management Module is now included in IceHrm Open Source Edition
|
||||||
* LDAP Module which was only available in IceHrm Enterprise is now included in open source also
|
* LDAP Module which was only available in IceHrm Enterprise is now included in open source also
|
||||||
* Initial implementation of icehrm REST Api for reading employee details
|
* Initial implementation of icehrm REST Api for reading employee details
|
||||||
@@ -268,30 +343,31 @@ Release note v16.0
|
|||||||
* Setting for updating module names
|
* Setting for updating module names
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix issue: classes should be loaded even the module is disabled
|
* Fix issue: classes should be loaded even the module is disabled
|
||||||
* Deleting the only Admin user is not allowed
|
* Deleting the only Admin user is not allowed
|
||||||
* Fixes for handling non UTF-8
|
* Fixes for handling non UTF-8
|
||||||
* Fix for non-mandatory select boxes are shown as mandatory
|
* Fix for non-mandatory select boxes are shown as mandatory
|
||||||
|
|
||||||
Release note v15.2
|
## Release note v15.2
|
||||||
------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Overtime Reports
|
* Overtime Reports
|
||||||
* Overtime calculation for california
|
* Overtime calculation for california
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix issue: uncaught error when placeholder value is empty
|
* Fix issue: uncaught error when placeholder value is empty
|
||||||
* Log email sending success status
|
* Log email sending success status
|
||||||
* Fix broken longer company name issue
|
* Fix broken longer company name issue
|
||||||
* Make the application accessible when client on an intranet with no internet connection
|
* Make the application accessible when client on an intranet with no internet connection
|
||||||
* Fix issue: when a module is disabled other modules depend on it stops working
|
* Fix issue: when a module is disabled other modules depend on it stops working
|
||||||
|
|
||||||
|
## Release note v15.0
|
||||||
Release note v15.0
|
|
||||||
------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Clear HTML5 local storage when logging in and switching users
|
* Clear HTML5 local storage when logging in and switching users
|
||||||
* Showing a loading message while getting data from server
|
* Showing a loading message while getting data from server
|
||||||
* Adding a new field to show total time of each time sheet
|
* Adding a new field to show total time of each time sheet
|
||||||
@@ -299,24 +375,26 @@ Release note v15.0
|
|||||||
* Company logo uploaded via settings will be used for all email headers
|
* Company logo uploaded via settings will be used for all email headers
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix issue: default module URL is incorrect for Employees
|
* Fix issue: default module URL is incorrect for Employees
|
||||||
* Fix date parsing issue in time sheets
|
* Fix date parsing issue in time sheets
|
||||||
* AWS phar is included only when required
|
* AWS phar is included only when required
|
||||||
|
|
||||||
Release note v14.1
|
## Release note v14.1
|
||||||
------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add Quick access menu
|
* Add Quick access menu
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix issue: salary module not loading
|
* Fix issue: salary module not loading
|
||||||
* Add travel report
|
* Add travel report
|
||||||
|
|
||||||
Release note v14.0
|
## Release note v14.0
|
||||||
------------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* IceHrm is now fully compatible with PHP 7
|
* IceHrm is now fully compatible with PHP 7
|
||||||
* Improvements to travel management module to change the process of applying for travel requests
|
* Improvements to travel management module to change the process of applying for travel requests
|
||||||
* New report add for getting travel requests
|
* New report add for getting travel requests
|
||||||
@@ -328,129 +406,125 @@ Release note v14.0
|
|||||||
* New reports added for employee expenses and travel
|
* New reports added for employee expenses and travel
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix unavailable help links
|
* Fix unavailable help links
|
||||||
|
|
||||||
|
## Release note v13.4
|
||||||
Release note v13.4
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix employee leave report leave type field
|
* Fix employee leave report leave type field
|
||||||
|
|
||||||
Release note v13.0
|
## Release note v13.0
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Recruitment module
|
* Recruitment module
|
||||||
* Allow managers to edit attendance of direct report employees
|
* Allow managers to edit attendance of direct report employees
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Employee switching issue fixed
|
* Employee switching issue fixed
|
||||||
* Fix terminated employee labels
|
* Fix terminated employee labels
|
||||||
* Fix issue with punch-in
|
* Fix issue with punch-in
|
||||||
|
|
||||||
Release note v12.6
|
## Release note v12.6
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Charts module
|
* Charts module
|
||||||
* Code level security improvements
|
* Code level security improvements
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Employee switching issue fixed
|
* Employee switching issue fixed
|
||||||
|
|
||||||
|
## Release note v11.1
|
||||||
Release note v11.1
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Add/Edit or remove employee fields
|
* Add/Edit or remove employee fields
|
||||||
|
|
||||||
|
## Release note v11.0
|
||||||
Release note v11.0
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Employee data archiving
|
* Employee data archiving
|
||||||
* Leave cancellation requests
|
* Leave cancellation requests
|
||||||
* Adding view employee feature
|
* Adding view employee feature
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Improvements to date time pickers
|
* Improvements to date time pickers
|
||||||
|
|
||||||
|
## Release note v10.1
|
||||||
Release note v10.1
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
* Integration with ice-framework (http://githun.com/thilinah/ice-framework)
|
|
||||||
|
* Integration with ice-framework \([http://githun.com/thilinah/ice-framework](http://githun.com/thilinah/ice-framework)\)
|
||||||
* Option for only allow users to add an entry to a timesheet only if they have marked atteandance for the selected period
|
* Option for only allow users to add an entry to a timesheet only if they have marked atteandance for the selected period
|
||||||
* Restricting availability of leave types to employees using leave groups
|
* Restricting availability of leave types to employees using leave groups
|
||||||
* Admins and add notes to employees
|
* Admins and add notes to employees
|
||||||
|
|
||||||
Release note v9.1
|
## Release note v9.1
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Add missing S3FileSystem class
|
* Add missing S3FileSystem class
|
||||||
* Fix issue: passing result of a method call directly into empty method is not supported in php v5.3
|
* Fix issue: passing result of a method call directly into empty method is not supported in php v5.3
|
||||||
|
|
||||||
|
## Release note v9.0
|
||||||
Release note v9.0
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* New user interface
|
* New user interface
|
||||||
* Decimal leave counts supported
|
* Decimal leave counts supported
|
||||||
|
|
||||||
Update icehrm v8.4 to v9.0
|
## Update icehrm v8.4 to v9.0
|
||||||
--------------------------
|
|
||||||
|
|
||||||
* Make a backup of your icehrm db
|
* Make a backup of your icehrm db
|
||||||
* Run db script "icehrmdb_update_v8.4_to_v9.0.sql" which can be found inside script folder of icehrm_v9.0
|
* Run db script "icehrmdb\_update\_v8.4\_to\_v9.0.sql" which can be found inside script folder of icehrm\_v9.0
|
||||||
* remove all folders except app folder in icehrm root folder
|
* remove all folders except app folder in icehrm root folder
|
||||||
* copy all folders except app folder from new installation to icehrm root folder
|
* copy all folders except app folder from new installation to icehrm root folder
|
||||||
|
|
||||||
|
## Release note v8.4
|
||||||
Release note v8.4
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fix leave carry forward rounding issues
|
* Fix leave carry forward rounding issues
|
||||||
* Fix issue: select2 default value not getting set for select2
|
* Fix issue: select2 default value not getting set for select2
|
||||||
* Fix issue: email not sent when admin changing leave status
|
* Fix issue: email not sent when admin changing leave status
|
||||||
|
|
||||||
Release note v8.3
|
## Release note v8.3
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* Fix user table issue on windows, this will resolve errors such as: (Note that this fix has no effect on unix based installations)
|
|
||||||
|
* Fix user table issue on windows, this will resolve errors such as: \(Note that this fix has no effect on unix based installations\)
|
||||||
* Admin not able to view user uploaded documents
|
* Admin not able to view user uploaded documents
|
||||||
* Admin not able to upload documants for users
|
* Admin not able to upload documants for users
|
||||||
* Admin can not view employee attendance records
|
* Admin can not view employee attendance records
|
||||||
* Employee projects can not be added
|
* Employee projects can not be added
|
||||||
|
|
||||||
|
## Release note v8.2
|
||||||
Release note v8.2
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Instance verification added
|
* Instance verification added
|
||||||
|
|
||||||
Release note v8.1
|
## Release note v8.1
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Fixed bug that caused a fatal error in php v5.4
|
* Fixed bug that caused a fatal error in php v5.4
|
||||||
* aws2.7.11 phar file replaced by a aws2.7.11 extracted files
|
* aws2.7.11 phar file replaced by a aws2.7.11 extracted files
|
||||||
* old aws sdk removed
|
* old aws sdk removed
|
||||||
|
|
||||||
Release note v8.0
|
## Release note v8.0
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Admin dashbord module
|
* Admin dashbord module
|
||||||
* If the employee joined in current leave period, his leave entitlement is calculated proportional to joined date
|
* If the employee joined in current leave period, his leave entitlement is calculated proportional to joined date
|
||||||
* Improvements to reporting module
|
* Improvements to reporting module
|
||||||
@@ -462,22 +536,22 @@ Release note v8.0
|
|||||||
* Upgrade aws sdk to v2.7.11
|
* Upgrade aws sdk to v2.7.11
|
||||||
* Allow employees to change password
|
* Allow employees to change password
|
||||||
* Use only the email address defined under user for sending mails
|
* Use only the email address defined under user for sending mails
|
||||||
* Making work_email and private_email fields optional
|
* Making work\_email and private\_email fields optional
|
||||||
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Upload dialog close button issue fixed
|
* Upload dialog close button issue fixed
|
||||||
|
|
||||||
Release note v7.2
|
## Release note v7.2
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* Some critical vulnerabilities are fixed as recommend by http://zeroscience.mk/en/
|
|
||||||
|
|
||||||
Release note v7.1
|
* Some critical vulnerabilities are fixed as recommend by [http://zeroscience.mk/en/](http://zeroscience.mk/en/)
|
||||||
-----------------
|
|
||||||
|
## Release note v7.1
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Improved company structure graph
|
* Improved company structure graph
|
||||||
* Leave notes implementation <20> Supervisor can add a note when approving or rejecting leaves
|
* Leave notes implementation <20> Supervisor can add a note when approving or rejecting leaves
|
||||||
* Filtering support
|
* Filtering support
|
||||||
@@ -486,20 +560,20 @@ Release note v7.1
|
|||||||
* Add ability to disable employee information editing
|
* Add ability to disable employee information editing
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
* Make loans editable only by admin
|
* Make loans editable only by admin
|
||||||
* Fix: permissions not getting applied to employee documents
|
* Fix: permissions not getting applied to employee documents
|
||||||
* Fix error adding employee documents when no user assigned to the admin
|
* Fix error adding employee documents when no user assigned to the admin
|
||||||
|
|
||||||
### Code Quality
|
### Code Quality
|
||||||
|
|
||||||
* Moving all module related code and data into module folders
|
* Moving all module related code and data into module folders
|
||||||
|
|
||||||
Release note v6.1
|
## Release note v6.1
|
||||||
-----------------
|
|
||||||
|
|
||||||
Leave carry forwared related isue fixed
|
Leave carry forwared related isue fixed
|
||||||
|
|
||||||
Release note v6.0
|
## Release note v6.0
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Features
|
* Features
|
||||||
* Notifications for leaves and timesheets
|
* Notifications for leaves and timesheets
|
||||||
@@ -513,21 +587,18 @@ Release note v6.0
|
|||||||
* Admin can make all projects available to employees or just the set of prjects assigned to them using Setting "Projects: Make All Projects Available to Employees"
|
* Admin can make all projects available to employees or just the set of prjects assigned to them using Setting "Projects: Make All Projects Available to Employees"
|
||||||
* Employee document, date added field can not be changed by the employee anymore
|
* Employee document, date added field can not be changed by the employee anymore
|
||||||
* About dialog added for admins
|
* About dialog added for admins
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Fix default employee delete issue (when the default employee is deleted the admin user attached to it also get deleted)
|
* Fix default employee delete issue \(when the default employee is deleted the admin user attached to it also get deleted\)
|
||||||
* Fix user duplicate email issue
|
* Fix user duplicate email issue
|
||||||
* Fix manager can not logout from switched employee
|
* Fix manager can not logout from switched employee
|
||||||
* Remove admin guide from non admin users
|
* Remove admin guide from non admin users
|
||||||
|
|
||||||
Release note v5.3
|
## Release note v5.3
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Fix missing employee name in employee details report
|
* Fix missing employee name in employee details report
|
||||||
|
|
||||||
Release note v5.2
|
## Release note v5.2
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Remove unwanted error logs
|
* Remove unwanted error logs
|
||||||
@@ -536,38 +607,33 @@ Release note v5.2
|
|||||||
* Remove add new button from subordinates module
|
* Remove add new button from subordinates module
|
||||||
* Adding administrators' guide
|
* Adding administrators' guide
|
||||||
|
|
||||||
Release note v5.1
|
## Release note v5.1
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Fixing for non updating null fields
|
* Fixing for non updating null fields
|
||||||
* https://bitbucket.org/thilina/icehrm-opensource/commits/df57308b53484a2e43ef5c72967ed1cd0dc756cc
|
* [https://bitbucket.org/thilina/icehrm-opensource/commits/df57308b53484a2e43ef5c72967ed1cd0dc756cc](https://bitbucket.org/thilina/icehrm-opensource/commits/df57308b53484a2e43ef5c72967ed1cd0dc756cc)
|
||||||
|
|
||||||
Release note v5.0
|
## Release note v5.0
|
||||||
-----------------
|
|
||||||
|
|
||||||
* Features
|
* Features
|
||||||
* New user permission implementation
|
* New user permission implementation
|
||||||
* Adding new user level - Manager
|
* Adding new user level - Manager
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Fixing remote table loading issue
|
* Fixing remote table loading issue
|
||||||
|
|
||||||
Release note v4.2
|
## Release note v4.2
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
* https://bitbucket.org/thilina/icehrm-opensource/issue/23/subordinate-leaves-pagination-not-working
|
|
||||||
* https://bitbucket.org/thilina/icehrm-opensource/issue/20/error-occured-while-time-punch
|
|
||||||
|
|
||||||
|
* [https://bitbucket.org/thilina/icehrm-opensource/issue/23/subordinate-leaves-pagination-not-working](https://bitbucket.org/thilina/icehrm-opensource/issue/23/subordinate-leaves-pagination-not-working)
|
||||||
|
* [https://bitbucket.org/thilina/icehrm-opensource/issue/20/error-occured-while-time-punch](https://bitbucket.org/thilina/icehrm-opensource/issue/20/error-occured-while-time-punch)
|
||||||
|
|
||||||
Release note v4.1
|
## Release note v4.1
|
||||||
-----------------
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Better email format for notifications
|
* Better email format for notifications
|
||||||
* Convert upload dialog to a bootstrp model
|
* Convert upload dialog to a bootstrp model
|
||||||
|
|
||||||
* Fixes
|
* Fixes
|
||||||
* Fix error sending emails with amazon SES
|
* Fix error sending emails with amazon SES
|
||||||
* Fix errors related to XAMPP and WAMPP servers
|
* Fix errors related to XAMPP and WAMPP servers
|
||||||
@@ -576,3 +642,4 @@ Release note v4.1
|
|||||||
* Allow icehrm client to work without an internet connection
|
* Allow icehrm client to work without an internet connection
|
||||||
* Fix installer incorrect base url issue
|
* Fix installer incorrect base url issue
|
||||||
* Fix empty user creation issue
|
* Fix empty user creation issue
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,21 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AdapterBase from '../../../api/AdapterBase';
|
import ReactModalAdapterBase from '../../../api/ReactModalAdapterBase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ClientAdapter
|
* ClientAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ClientAdapter extends AdapterBase {
|
class ClientAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -30,6 +38,32 @@ class ClientAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Details',
|
||||||
|
dataIndex: 'details',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Address',
|
||||||
|
dataIndex: 'address',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Contact Number',
|
||||||
|
dataIndex: 'contact_number',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
if (this.showSave) {
|
if (this.showSave) {
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { CompanyStructureAdapter, CompanyGraphAdapter } from './lib';
|
import { CompanyStructureAdapter, CompanyGraphAdapter } from './lib';
|
||||||
import IceDataPipe from "../../../api/IceDataPipe";
|
import IceDataPipe from "../../../api/IceDataPipe";
|
||||||
import CustomFieldAdapter from "../../../api/ReactCustomFieldAdapter";
|
|
||||||
|
|
||||||
|
|
||||||
function init(data) {
|
function init(data) {
|
||||||
@@ -9,6 +8,7 @@ function init(data) {
|
|||||||
modJsList.tabCompanyStructure.setObjectTypeName('Company Structure');
|
modJsList.tabCompanyStructure.setObjectTypeName('Company Structure');
|
||||||
modJsList.tabCompanyStructure.setDataPipe(new IceDataPipe(modJsList.tabCompanyStructure));
|
modJsList.tabCompanyStructure.setDataPipe(new IceDataPipe(modJsList.tabCompanyStructure));
|
||||||
modJsList.tabCompanyStructure.setAccess(data.permissions.CompanyStructure);
|
modJsList.tabCompanyStructure.setAccess(data.permissions.CompanyStructure);
|
||||||
|
modJsList.tabCompanyStructure.setCustomFields(data.customFields);
|
||||||
|
|
||||||
modJsList.tabCompanyGraph = new CompanyGraphAdapter('CompanyStructure');
|
modJsList.tabCompanyGraph = new CompanyGraphAdapter('CompanyStructure');
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class CompanyStructureAdapter extends ReactModalAdapterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return this.addCustomFields([
|
||||||
['id', { label: 'ID', type: 'hidden', validation: '' }],
|
['id', { label: 'ID', type: 'hidden', validation: '' }],
|
||||||
['title', { label: 'Name', type: 'text', validation: '' }],
|
['title', { label: 'Name', type: 'text', validation: '' }],
|
||||||
['description', { label: 'Details', type: 'textarea', validation: '' }],
|
['description', { label: 'Details', type: 'textarea', validation: '' }],
|
||||||
@@ -80,7 +80,7 @@ class CompanyStructureAdapter extends ReactModalAdapterBase {
|
|||||||
['heads', {
|
['heads', {
|
||||||
label: 'Heads', type: 'select2multi', 'allow-null': true, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
label: 'Heads', type: 'select2multi', 'allow-null': true, 'remote-source': ['Employee', 'id', 'first_name+last_name'],
|
||||||
}],
|
}],
|
||||||
];
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
postRenderForm(object, $tempDomObj) {
|
postRenderForm(object, $tempDomObj) {
|
||||||
|
|||||||
18
web/admin/src/custom_fields/index.js
Normal file
18
web/admin/src/custom_fields/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { CommonCustomFieldAdapter } from './lib';
|
||||||
|
import IceDataPipe from '../../../api/IceDataPipe';
|
||||||
|
|
||||||
|
function init(data) {
|
||||||
|
const modJsList = {};
|
||||||
|
|
||||||
|
modJsList.tabCustomField = new CommonCustomFieldAdapter('CustomField', 'CustomField', {}, '');
|
||||||
|
modJsList.tabCustomField.setRemoteTable(true);
|
||||||
|
modJsList.tabCustomField.setObjectTypeName('Custom Field');
|
||||||
|
modJsList.tabCustomField.setDataPipe(new IceDataPipe(modJsList.tabCustomField));
|
||||||
|
modJsList.tabCustomField.setAccess(data.permissions.CustomField);
|
||||||
|
modJsList.tabCustomField.setTypes(data.types);
|
||||||
|
|
||||||
|
window.modJs = modJsList.tabCustomField;
|
||||||
|
window.modJsList = modJsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initAdminCustomFields = init;
|
||||||
166
web/admin/src/custom_fields/lib.js
Normal file
166
web/admin/src/custom_fields/lib.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Author: Thilina Hasantha
|
||||||
|
*/
|
||||||
|
import ReactCustomFieldAdapter from '../../../api/ReactCustomFieldAdapter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AssetTypeAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CommonCustomFieldAdapter extends ReactCustomFieldAdapter {
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'type',
|
||||||
|
'field_type',
|
||||||
|
'field_label',
|
||||||
|
'display',
|
||||||
|
'display_order',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Object Type' },
|
||||||
|
{ sTitle: 'Field Type' },
|
||||||
|
{ sTitle: 'Field Label' },
|
||||||
|
{ sTitle: 'Display Status' },
|
||||||
|
{ sTitle: 'Priority' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Object Type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Field Label',
|
||||||
|
dataIndex: 'field_label',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Field Type',
|
||||||
|
dataIndex: 'field_type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Display Status',
|
||||||
|
dataIndex: 'display',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Priority',
|
||||||
|
dataIndex: 'display_order',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setTypes(tables) {
|
||||||
|
this.types = tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
||||||
|
['type',
|
||||||
|
{
|
||||||
|
label: 'Object Type',
|
||||||
|
type: 'select2',
|
||||||
|
source: this.types,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time'], ['signature', 'Signature']] }],
|
||||||
|
['field_validation', {
|
||||||
|
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
||||||
|
}],
|
||||||
|
['field_options', {
|
||||||
|
label: 'Field Options',
|
||||||
|
type: 'datagroup',
|
||||||
|
form: [
|
||||||
|
['label', { label: 'Label', type: 'text', validation: '' }],
|
||||||
|
['value', { label: 'Value', type: 'text', validation: 'none' }],
|
||||||
|
],
|
||||||
|
html: '<div id="#_id_#" class="panel panel-default"><div class="panel-body">#_delete_##_edit_#<span style="color:#999;font-size:13px;font-weight:bold">#_label_#</span>:#_value_#</div></div>',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: 'Label',
|
||||||
|
dataIndex: 'label',
|
||||||
|
key: 'label',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Option Value',
|
||||||
|
dataIndex: 'value',
|
||||||
|
key: 'value',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
validation: 'none',
|
||||||
|
}],
|
||||||
|
['display_order', { label: 'Priority', type: 'text', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters() {
|
||||||
|
return [
|
||||||
|
['type',
|
||||||
|
{
|
||||||
|
label: 'Object Type',
|
||||||
|
type: 'select2',
|
||||||
|
'allow-null': true,
|
||||||
|
source: this.types,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
forceInjectValuesBeforeSave(params) {
|
||||||
|
const data = ['', {}];
|
||||||
|
const options = [];
|
||||||
|
let optionsData;
|
||||||
|
|
||||||
|
data[1].label = params.field_label;
|
||||||
|
data[1].type = params.field_type;
|
||||||
|
data[1].validation = params.field_validation;
|
||||||
|
if (['select', 'select2', 'select2multi'].indexOf(params.field_type) >= 0) {
|
||||||
|
optionsData = (params.field_options === '' || params.field_options === undefined)
|
||||||
|
? [] : JSON.parse(params.field_options);
|
||||||
|
for (const index in optionsData) {
|
||||||
|
options.push([optionsData[index].value, optionsData[index].label]);
|
||||||
|
}
|
||||||
|
data[1].source = options;
|
||||||
|
}
|
||||||
|
if (params.field_validation == null || params.field_validation === undefined) {
|
||||||
|
params.field_validation = '';
|
||||||
|
}
|
||||||
|
if (this.currentElement == null || this.currentElement.name == null || this.currentElement.name === '') {
|
||||||
|
params.name = this.getNameFromFieldName(params.field_label);
|
||||||
|
} else {
|
||||||
|
params.name = this.currentElement.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = params.name;
|
||||||
|
params.data = JSON.stringify(data);
|
||||||
|
|
||||||
|
params.display = 'Form';
|
||||||
|
|
||||||
|
params.display_order = parseInt(params.display_order);
|
||||||
|
if (!Number.isInteger(params.display_order)) {
|
||||||
|
params.display_order = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = { CommonCustomFieldAdapter };
|
||||||
@@ -98,6 +98,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawEmployeeDistributionChart() {
|
drawEmployeeDistributionChart() {
|
||||||
|
const that = this;
|
||||||
document.getElementById('EmployeeDistributionChart').style.display = 'none';
|
document.getElementById('EmployeeDistributionChart').style.display = 'none';
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
this.getSpinner(),
|
this.getSpinner(),
|
||||||
@@ -115,7 +116,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
forceFit: true,
|
forceFit: true,
|
||||||
title: {
|
title: {
|
||||||
visible: true,
|
visible: true,
|
||||||
text: 'Employee Distribution',
|
text: that.gt('Employee Distribution'),
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -125,7 +126,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
visible: true,
|
visible: true,
|
||||||
content: {
|
content: {
|
||||||
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
||||||
name: 'Total',
|
name: that.gt('Total'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@@ -151,6 +152,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawOnlineOfflineEmployeeChart() {
|
drawOnlineOfflineEmployeeChart() {
|
||||||
|
const that = this;
|
||||||
document.getElementById('EmployeeOnlineOfflineChart').style.display = 'none';
|
document.getElementById('EmployeeOnlineOfflineChart').style.display = 'none';
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
this.getSpinner(),
|
this.getSpinner(),
|
||||||
@@ -168,7 +170,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
forceFit: true,
|
forceFit: true,
|
||||||
title: {
|
title: {
|
||||||
visible: true,
|
visible: true,
|
||||||
text: 'Employee Check-Ins',
|
text: that.gt('Employee Check-Ins'),
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -178,7 +180,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
visible: true,
|
visible: true,
|
||||||
content: {
|
content: {
|
||||||
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
||||||
name: 'Total',
|
name: that.gt('Total'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
@@ -199,6 +201,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
drawCompanyLeaveEntitlementChart() {
|
drawCompanyLeaveEntitlementChart() {
|
||||||
|
const that = this;
|
||||||
document.getElementById('CompanyLeaveEntitlementChart').style.display = 'none';
|
document.getElementById('CompanyLeaveEntitlementChart').style.display = 'none';
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
this.getSpinner(),
|
this.getSpinner(),
|
||||||
@@ -216,7 +219,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
forceFit: true,
|
forceFit: true,
|
||||||
title: {
|
title: {
|
||||||
visible: true,
|
visible: true,
|
||||||
text: 'Company Vacation Usage',
|
text: that.gt('Company Vacation Usage'),
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
visible: false,
|
visible: false,
|
||||||
@@ -226,7 +229,7 @@ class DashboardAdapter extends AdapterBase {
|
|||||||
visible: true,
|
visible: true,
|
||||||
content: {
|
content: {
|
||||||
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
value: chartData.reduce((acc, item) => acc + item.value, 0),
|
||||||
name: 'Total',
|
name: that.gt('Total'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ class DocumentAdapter extends AdapterBase {
|
|||||||
['expire_notification_month', { label: 'Notify Expiry Before One Month', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
['expire_notification_month', { label: 'Notify Expiry Before One Month', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
['expire_notification_week', { label: 'Notify Expiry Before One Week', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
['expire_notification_week', { label: 'Notify Expiry Before One Week', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
['expire_notification_day', { label: 'Notify Expiry Before One Day', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
['expire_notification_day', { label: 'Notify Expiry Before One Day', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
|
['share_with_employee', { label: 'Share with Employee', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
|
// [ "sign", {"label":"Require Signature","type":"select","source":[["Yes","Yes"],["No","No"]]}],
|
||||||
// [ "sign", {"label":"Require Signature","type":"select","source":[["Yes","Yes"],["No","No"]]}],
|
// [ "sign", {"label":"Require Signature","type":"select","source":[["Yes","Yes"],["No","No"]]}],
|
||||||
// [ "sign_label", {"label":"Signature Description","type":"textarea","validation":"none"}],
|
// [ "sign_label", {"label":"Signature Description","type":"textarea","validation":"none"}],
|
||||||
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
@@ -146,6 +148,7 @@ class EmployeeDocumentAdapter extends AdapterBase {
|
|||||||
['date_added', { label: 'Date Added', type: 'date', validation: '' }],
|
['date_added', { label: 'Date Added', type: 'date', validation: '' }],
|
||||||
['valid_until', { label: 'Valid Until', type: 'date', validation: 'none' }],
|
['valid_until', { label: 'Valid Until', type: 'date', validation: 'none' }],
|
||||||
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['Inactive', 'Inactive'], ['Draft', 'Draft']] }],
|
['status', { label: 'Status', type: 'select', source: [['Active', 'Active'], ['Inactive', 'Inactive'], ['Draft', 'Draft']] }],
|
||||||
|
['visible_to', { label: 'Visible To', type: 'select', source: [['Owner', 'Owner'], ['Manager', 'Manager'], ['Admin', 'Admin']] }],
|
||||||
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
['attachment', { label: 'Attachment', type: 'fileupload', validation: '' }],
|
['attachment', { label: 'Attachment', type: 'fileupload', validation: '' }],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -363,28 +363,7 @@ class EmployeeAdapter extends ReactModalAdapterBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.addActualFields(steps, fields);
|
return this.addActualFieldsForStepModal(steps, fields);
|
||||||
}
|
|
||||||
|
|
||||||
addActualFields(steps, fields) {
|
|
||||||
return steps.map((item) => {
|
|
||||||
item.fields = item.fields.reduce((acc, fieldName) => {
|
|
||||||
const field = fields.find(([name]) => name === fieldName);
|
|
||||||
if (field) {
|
|
||||||
acc.push(field);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getFormOptions() {
|
|
||||||
return {
|
|
||||||
width: 1024,
|
|
||||||
twoColumnLayout: false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilters() {
|
getFilters() {
|
||||||
@@ -426,7 +405,7 @@ class EmployeeAdapter extends ReactModalAdapterBase {
|
|||||||
&& (
|
&& (
|
||||||
<Tag color="volcano" onClick={() => modJs.terminateEmployee(record.id)} style={{ cursor: 'pointer' }}>
|
<Tag color="volcano" onClick={() => modJs.terminateEmployee(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
{` ${adapter.gt('Delete')}`}
|
{` ${adapter.gt('Deactivate')}`}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{adapter.hasAccess('save')
|
{adapter.hasAccess('save')
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { FieldNameAdapter, CustomFieldAdapter } from './lib';
|
import { FieldNameAdapter } from './lib';
|
||||||
|
|
||||||
window.FieldNameAdapter = FieldNameAdapter;
|
window.FieldNameAdapter = FieldNameAdapter;
|
||||||
window.CustomFieldAdapter = CustomFieldAdapter;
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import AdapterBase from '../../../api/AdapterBase';
|
import AdapterBase from '../../../api/AdapterBase';
|
||||||
import CustomFieldAdapter from '../../../api/CustomFieldAdapter';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FieldNameAdapter
|
* FieldNameAdapter
|
||||||
@@ -44,4 +43,4 @@ class FieldNameAdapter extends AdapterBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
module.exports = { FieldNameAdapter, CustomFieldAdapter };
|
module.exports = { FieldNameAdapter };
|
||||||
|
|||||||
@@ -3,5 +3,24 @@ import {
|
|||||||
EmployeeCompanyLoanAdapter,
|
EmployeeCompanyLoanAdapter,
|
||||||
} from './lib';
|
} from './lib';
|
||||||
|
|
||||||
window.CompanyLoanAdapter = CompanyLoanAdapter;
|
|
||||||
window.EmployeeCompanyLoanAdapter = EmployeeCompanyLoanAdapter;
|
import IceDataPipe from "../../../api/IceDataPipe";
|
||||||
|
|
||||||
|
|
||||||
|
function init(data) {
|
||||||
|
const modJsList = {};
|
||||||
|
modJsList.tabCompanyLoan = new CompanyLoanAdapter('CompanyLoan', 'CompanyLoan');
|
||||||
|
modJsList.tabCompanyLoan.setObjectTypeName('Company Loan');
|
||||||
|
modJsList.tabCompanyLoan.setDataPipe(new IceDataPipe(modJsList.tabCompanyLoan));
|
||||||
|
modJsList.tabCompanyLoan.setAccess(data.permissions.CompanyLoan);
|
||||||
|
|
||||||
|
modJsList.tabEmployeeCompanyLoan = new EmployeeCompanyLoanAdapter('EmployeeCompanyLoan', 'EmployeeCompanyLoan');
|
||||||
|
modJsList.tabEmployeeCompanyLoan.setObjectTypeName('Employee Company Loan');
|
||||||
|
modJsList.tabEmployeeCompanyLoan.setDataPipe(new IceDataPipe(modJsList.tabEmployeeCompanyLoan));
|
||||||
|
modJsList.tabEmployeeCompanyLoan.setAccess(data.permissions.EmployeeCompanyLoan);
|
||||||
|
|
||||||
|
window.modJs = modJsList.tabCompanyLoan;
|
||||||
|
window.modJsList = modJsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initAdminLoan = init;
|
||||||
|
|||||||
@@ -2,13 +2,22 @@
|
|||||||
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
import AdapterBase from '../../../api/AdapterBase';
|
//import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import ReactModalAdapterBase from '../../../api/ReactModalAdapterBase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompanyLoanAdapter
|
* CompanyLoanAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class CompanyLoanAdapter extends AdapterBase {
|
class CompanyLoanAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -25,7 +34,29 @@ class CompanyLoanAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Details',
|
||||||
|
dataIndex: 'details',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
|
if(this.showSave){
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
['details', { label: 'Details', type: 'textarea', validation: 'none' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
['name', { label: 'Name', type: 'text', validation: '' }],
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
@@ -39,7 +70,15 @@ class CompanyLoanAdapter extends AdapterBase {
|
|||||||
* EmployeeCompanyLoanAdapter
|
* EmployeeCompanyLoanAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class EmployeeCompanyLoanAdapter extends AdapterBase {
|
class EmployeeCompanyLoanAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -66,6 +105,46 @@ class EmployeeCompanyLoanAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Employee',
|
||||||
|
dataIndex: 'employee',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Loan Type',
|
||||||
|
dataIndex: 'loan',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Loan Start Date',
|
||||||
|
dataIndex: 'start_date',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Loan Period (Months)',
|
||||||
|
dataIndex: 'period_months',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Currency',
|
||||||
|
dataIndex: 'currency',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Amount',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Status',
|
||||||
|
dataIndex: 'status',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
@@ -97,5 +176,5 @@ class EmployeeCompanyLoanAdapter extends AdapterBase {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
CompanyLoanAdapter,
|
CompanyLoanAdapter,
|
||||||
EmployeeCompanyLoanAdapter,
|
EmployeeCompanyLoanAdapter
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -344,7 +344,7 @@ class PayrollColumnAdapter extends AdapterBase {
|
|||||||
['enabled', { label: 'Enabled', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
['enabled', { label: 'Enabled', type: 'select', source: [['Yes', 'Yes'], ['No', 'No']] }],
|
||||||
['default_value', { label: 'Default Value', type: 'text', validation: '' }],
|
['default_value', { label: 'Default Value', type: 'text', validation: '' }],
|
||||||
fucntionColumnList,
|
fucntionColumnList,
|
||||||
['function_type', { label: 'Function Type', type: 'select', source: [['Advanced', 'Advanced'], ['Simple', 'Simple']] }],
|
['function_type', { label: 'Function Type', type: 'select', source: [['Simple', 'Simple']] }],
|
||||||
['calculation_function', { label: 'Function', type: 'code', validation: 'none' }],
|
['calculation_function', { label: 'Function', type: 'code', validation: 'none' }],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,21 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AdapterBase from '../../../api/AdapterBase';
|
import ReactModalAdapterBase from '../../../api/ReactModalAdapterBase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ProjectAdapter
|
* ProjectAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ProjectAdapter extends AdapterBase {
|
class ProjectAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -26,6 +34,21 @@ class ProjectAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Client',
|
||||||
|
dataIndex: 'client',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
if (this.showSave) {
|
if (this.showSave) {
|
||||||
return [
|
return [
|
||||||
@@ -60,7 +83,15 @@ class ProjectAdapter extends AdapterBase {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
class EmployeeProjectAdapter extends AdapterBase {
|
class EmployeeProjectAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -77,6 +108,22 @@ class EmployeeProjectAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Employee',
|
||||||
|
dataIndex: 'employee',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project',
|
||||||
|
dataIndex: 'project',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
|||||||
@@ -4,6 +4,33 @@ import {
|
|||||||
EmployeeSalaryAdapter,
|
EmployeeSalaryAdapter,
|
||||||
} from './lib';
|
} from './lib';
|
||||||
|
|
||||||
window.SalaryComponentTypeAdapter = SalaryComponentTypeAdapter;
|
/*window.SalaryComponentTypeAdapter = SalaryComponentTypeAdapter;
|
||||||
window.SalaryComponentAdapter = SalaryComponentAdapter;
|
window.SalaryComponentAdapter = SalaryComponentAdapter;
|
||||||
window.EmployeeSalaryAdapter = EmployeeSalaryAdapter;
|
window.EmployeeSalaryAdapter = EmployeeSalaryAdapter;*/
|
||||||
|
|
||||||
|
import IceDataPipe from "../../../api/IceDataPipe";
|
||||||
|
|
||||||
|
|
||||||
|
function init(data) {
|
||||||
|
const modJsList = [];
|
||||||
|
modJsList.tabSalaryComponentType = new SalaryComponentTypeAdapter('SalaryComponentType', 'SalaryComponentType');
|
||||||
|
modJsList.tabSalaryComponentType.setObjectTypeName('Salary Component Types');
|
||||||
|
modJsList.tabSalaryComponentType.setDataPipe(new IceDataPipe(modJsList.tabSalaryComponentType));
|
||||||
|
modJsList.tabSalaryComponentType.setAccess(data.permissions.SalaryComponentType);
|
||||||
|
|
||||||
|
modJsList.tabSalaryComponent = new SalaryComponentAdapter('SalaryComponent', 'SalaryComponent');
|
||||||
|
modJsList.tabSalaryComponent.setObjectTypeName('Salary components');
|
||||||
|
modJsList.tabSalaryComponent.setDataPipe(new IceDataPipe(modJsList.tabSalaryComponent));
|
||||||
|
modJsList.tabSalaryComponent.setAccess(data.permissions.SalaryComponent);
|
||||||
|
|
||||||
|
modJsList.tabEmployeeSalary = new EmployeeSalaryAdapter('EmployeeSalary', 'EmployeeSalary');
|
||||||
|
modJsList.tabEmployeeSalary.setObjectTypeName('Employee Salary');
|
||||||
|
modJsList.tabEmployeeSalary.setDataPipe(new IceDataPipe(modJsList.tabEmployeeSalary));
|
||||||
|
modJsList.tabEmployeeSalary.setAccess(data.permissions.EmployeeSalary);
|
||||||
|
|
||||||
|
//window.modJs = modJsList.tabEmployeeSalary;
|
||||||
|
window.modJs = modJsList.tabSalaryComponentType;
|
||||||
|
window.modJsList = modJsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initAdminSalary = init;
|
||||||
@@ -3,13 +3,22 @@
|
|||||||
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import AdapterBase from '../../../api/AdapterBase';
|
//import AdapterBase from '../../../api/AdapterBase';
|
||||||
|
import ReactModalAdapterBase from '../../../api/ReactModalAdapterBase';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SalaryComponentTypeAdapter
|
* SalaryComponentTypeAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SalaryComponentTypeAdapter extends AdapterBase {
|
class SalaryComponentTypeAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -26,6 +35,21 @@ class SalaryComponentTypeAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Code',
|
||||||
|
dataIndex: 'code',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
@@ -40,7 +64,15 @@ class SalaryComponentTypeAdapter extends AdapterBase {
|
|||||||
* SalaryComponentAdapter
|
* SalaryComponentAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SalaryComponentAdapter extends AdapterBase {
|
class SalaryComponentAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -59,6 +91,27 @@ class SalaryComponentAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Salary Component Type',
|
||||||
|
dataIndex: 'componentType',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Details',
|
||||||
|
dataIndex: 'details',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
@@ -74,7 +127,15 @@ class SalaryComponentAdapter extends AdapterBase {
|
|||||||
* EmployeeSalaryAdapter
|
* EmployeeSalaryAdapter
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class EmployeeSalaryAdapter extends AdapterBase {
|
class EmployeeSalaryAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
this.fieldNameMap = {};
|
||||||
|
this.hiddenFields = {};
|
||||||
|
this.tableFields = {};
|
||||||
|
this.formOnlyFields = {};
|
||||||
|
}
|
||||||
|
|
||||||
getDataMapping() {
|
getDataMapping() {
|
||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
@@ -95,6 +156,31 @@ class EmployeeSalaryAdapter extends AdapterBase {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Employee',
|
||||||
|
dataIndex: 'employee',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Salary Component',
|
||||||
|
dataIndex: 'component',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Amount',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Details',
|
||||||
|
dataIndex: 'details',
|
||||||
|
sorter: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ import TimeUtils from './TimeUtils';
|
|||||||
import RequestCache from './RequestCache';
|
import RequestCache from './RequestCache';
|
||||||
import SocialShare from './SocialShare';
|
import SocialShare from './SocialShare';
|
||||||
|
|
||||||
const Aes = require('./Aes');
|
|
||||||
|
|
||||||
window.RequestCache = RequestCache;
|
window.RequestCache = RequestCache;
|
||||||
window.SocialShare = SocialShare;
|
window.SocialShare = SocialShare;
|
||||||
|
|
||||||
|
|||||||
@@ -104,16 +104,16 @@ class ApproveAdminAdapter extends LogViewAdapter {
|
|||||||
|
|
||||||
getStatusOptionsData(currentStatus) {
|
getStatusOptionsData(currentStatus) {
|
||||||
const data = {};
|
const data = {};
|
||||||
if (currentStatus == 'Approved') {
|
if (currentStatus === 'Approved') {
|
||||||
|
|
||||||
} else if (currentStatus == 'Pending') {
|
} else if (currentStatus === 'Pending') {
|
||||||
data.Approved = 'Approved';
|
data.Approved = 'Approved';
|
||||||
data.Rejected = 'Rejected';
|
data.Rejected = 'Rejected';
|
||||||
} else if (currentStatus == 'Rejected') {
|
} else if (currentStatus === 'Rejected') {
|
||||||
|
|
||||||
} else if (currentStatus == 'Cancelled') {
|
} else if (currentStatus === 'Cancelled') {
|
||||||
|
|
||||||
} else if (currentStatus == 'Processing') {
|
} else if (currentStatus === 'Processing') {
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
data['Cancellation Requested'] = 'Cancellation Requested';
|
data['Cancellation Requested'] = 'Cancellation Requested';
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CustomFieldAdapter extends AdapterBase {
|
|||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
['name', { label: 'Name', type: 'text', validation: '' }],
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
['display', { label: 'Display Status', type: 'select', source: [['Form', 'Show'], ['Hidden', 'Hidden']] }],
|
['display', { label: 'Display Status', type: 'select', source: [['Form', 'Show'], ['Hidden', 'Hidden']] }],
|
||||||
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time']] }],
|
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time'], ['signature', 'Signature']] }],
|
||||||
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
||||||
['field_validation', {
|
['field_validation', {
|
||||||
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ class ModuleBase {
|
|||||||
|
|
||||||
gt(key) {
|
gt(key) {
|
||||||
if (this.translations[key] === undefined || this.translations[key] === null) {
|
if (this.translations[key] === undefined || this.translations[key] === null) {
|
||||||
|
console.log("Tr:" + key);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
return this.translations[key][0];
|
return this.translations[key][0];
|
||||||
@@ -1057,7 +1058,7 @@ class ModuleBase {
|
|||||||
return this.gt('Password too short');
|
return this.gt('Password too short');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password.length > 20) {
|
if (password.length > 30) {
|
||||||
return this.gt('Password too long');
|
return this.gt('Password too long');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
134
web/api/ReactApproveAdminAdapter.js
Normal file
134
web/api/ReactApproveAdminAdapter.js
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReactApproveAdminAdapter
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import ReactLogViewAdapter from './ReactLogViewAdapter';
|
||||||
|
import {Space, Tag} from "antd";
|
||||||
|
import {CopyOutlined, DeleteOutlined, EditOutlined, MonitorOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
class ReactApproveAdminAdapter extends ReactLogViewAdapter {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusFieldPosition() {
|
||||||
|
const dm = this.getDataMapping();
|
||||||
|
return dm.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
openStatus(id, status) {
|
||||||
|
$(`#${this.itemNameLower}StatusModel`).modal('show');
|
||||||
|
$(`#${this.itemNameLower}_status`).html(this.getStatusOptions(status));
|
||||||
|
$(`#${this.itemNameLower}_status`).val(status);
|
||||||
|
this.statusChangeId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
$(`#${this.itemNameLower}StatusModel`).modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatus() {
|
||||||
|
const status = $(`#${this.itemNameLower}_status`).val();
|
||||||
|
const reason = $(`#${this.itemNameLower}_reason`).val();
|
||||||
|
|
||||||
|
if (status == undefined || status == null || status == '') {
|
||||||
|
this.showMessage('Error', `Please select ${this.itemNameLower} status`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const object = { id: this.statusChangeId, status, reason };
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'changeStatusSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'changeStatusFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('changeStatus', `admin=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
|
||||||
|
this.closeDialog();
|
||||||
|
this.statusChangeId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatusSuccessCallBack(callBackData) {
|
||||||
|
this.showMessage('Successful', `${this.itemName} Request status changed successfully`);
|
||||||
|
this.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeStatusFailCallBack(callBackData) {
|
||||||
|
this.showMessage('Error', `Error occurred while changing ${this.itemName} request status`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableActionButtonJsx(adapter) {
|
||||||
|
return (text, record) => (
|
||||||
|
<Space size="middle">
|
||||||
|
{adapter.hasAccess('save') && adapter.showEdit
|
||||||
|
&& (
|
||||||
|
<Tag color="green" onClick={() => modJs.edit(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<EditOutlined />
|
||||||
|
{` ${adapter.gt('Edit')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{adapter.hasAccess('delete') && adapter.showDelete
|
||||||
|
&& (
|
||||||
|
<Tag color="volcano" onClick={() => modJs.deleteRow(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<DeleteOutlined />
|
||||||
|
{` ${adapter.gt('Delete')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{Object.keys(this.getStatusOptionsData(record.status)).length > 0
|
||||||
|
&& (
|
||||||
|
<Tag color="blue" onClick={() => modJs.openStatus(record.id, record.status)} style={{ cursor: 'pointer' }}>
|
||||||
|
<MonitorOutlined />
|
||||||
|
{` ${adapter.gt('Change Status')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
<Tag color="cyan" onClick={() => modJs.getLogs(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<CopyOutlined />
|
||||||
|
{` ${adapter.gt('View Logs')}`}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCustomButtons() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSubProfileTable() {
|
||||||
|
return this.user.user_level !== 'Admin' && this.user.user_level !== 'Restricted Admin';
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptionsData(currentStatus) {
|
||||||
|
const data = {};
|
||||||
|
if (currentStatus === 'Approved') {
|
||||||
|
|
||||||
|
} else if (currentStatus === 'Pending') {
|
||||||
|
data.Approved = 'Approved';
|
||||||
|
data.Rejected = 'Rejected';
|
||||||
|
} else if (currentStatus === 'Rejected') {
|
||||||
|
|
||||||
|
} else if (currentStatus === 'Cancelled') {
|
||||||
|
|
||||||
|
} else if (currentStatus === 'Processing') {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data['Cancellation Requested'] = 'Cancellation Requested';
|
||||||
|
data.Cancelled = 'Cancelled';
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStatusOptions(currentStatus) {
|
||||||
|
return this.generateOptions(this.getStatusOptionsData(currentStatus));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactApproveAdminAdapter;
|
||||||
71
web/api/ReactApproveModuleAdapter.js
Normal file
71
web/api/ReactApproveModuleAdapter.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
import { Space, Tag } from 'antd';
|
||||||
|
import {
|
||||||
|
CopyOutlined, DeleteOutlined, EditOutlined, MonitorOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
import ReactLogViewAdapter from './ReactLogViewAdapter';
|
||||||
|
|
||||||
|
class ReactApproveModuleAdapter extends ReactLogViewAdapter {
|
||||||
|
cancelRequest(id) {
|
||||||
|
const object = {};
|
||||||
|
object.id = id;
|
||||||
|
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'cancelSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'cancelFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('cancel', `modules=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
cancelSuccessCallBack(callBackData) {
|
||||||
|
this.showMessage('Successful', `${this.itemName} cancellation request sent`);
|
||||||
|
this.get([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelFailCallBack(callBackData) {
|
||||||
|
this.showMessage(`Error Occurred while cancelling ${this.itemName}`, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableActionButtonJsx(adapter) {
|
||||||
|
return (text, record) => (
|
||||||
|
<Space size="middle">
|
||||||
|
{adapter.hasAccess('save') && adapter.showEdit
|
||||||
|
&& (
|
||||||
|
<Tag color="green" onClick={() => modJs.edit(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<EditOutlined />
|
||||||
|
{` ${adapter.gt('Edit')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{adapter.hasAccess('delete') && adapter.showDelete && record.status === 'Approved'
|
||||||
|
&& (
|
||||||
|
<Tag color="volcano" onClick={() => modJs.cancelRequest(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<DeleteOutlined />
|
||||||
|
{` ${adapter.gt('Cancel')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
{adapter.hasAccess('delete') && adapter.showDelete && record.status === 'Pending'
|
||||||
|
&& this.user.user_level === 'Admin'
|
||||||
|
&& (
|
||||||
|
<Tag color="volcano" onClick={() => modJs.deleteRow(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<DeleteOutlined />
|
||||||
|
{` ${adapter.gt('Delete')}`}
|
||||||
|
</Tag>
|
||||||
|
)}
|
||||||
|
<Tag color="cyan" onClick={() => modJs.getLogs(record.id)} style={{ cursor: 'pointer' }}>
|
||||||
|
<CopyOutlined />
|
||||||
|
{` ${adapter.gt('View Logs')}`}
|
||||||
|
</Tag>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactApproveModuleAdapter;
|
||||||
@@ -20,6 +20,8 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
return [
|
return [
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
|
'field_type',
|
||||||
|
'field_label',
|
||||||
'display',
|
'display',
|
||||||
'display_order',
|
'display_order',
|
||||||
];
|
];
|
||||||
@@ -29,6 +31,8 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
return [
|
return [
|
||||||
{ sTitle: 'ID', bVisible: false },
|
{ sTitle: 'ID', bVisible: false },
|
||||||
{ sTitle: 'Name' },
|
{ sTitle: 'Name' },
|
||||||
|
{ sTitle: 'Field Type' },
|
||||||
|
{ sTitle: 'Field Label' },
|
||||||
{ sTitle: 'Display Status' },
|
{ sTitle: 'Display Status' },
|
||||||
{ sTitle: 'Priority' },
|
{ sTitle: 'Priority' },
|
||||||
];
|
];
|
||||||
@@ -40,17 +44,24 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
title: 'Name',
|
title: 'Name',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
width: '25%',
|
},
|
||||||
|
{
|
||||||
|
title: 'Field Label',
|
||||||
|
dataIndex: 'field_label',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Field Type',
|
||||||
|
dataIndex: 'field_type',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Display Status',
|
title: 'Display Status',
|
||||||
dataIndex: 'display',
|
dataIndex: 'display',
|
||||||
width: '35%',
|
sorter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Priority',
|
title: 'Priority',
|
||||||
dataIndex: 'display_order',
|
dataIndex: 'display_order',
|
||||||
width: '10%',
|
sorter: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -58,10 +69,8 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
getFormFields() {
|
getFormFields() {
|
||||||
return [
|
return [
|
||||||
['id', { label: 'ID', type: 'hidden' }],
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
['name', { label: 'Name', type: 'text', validation: '' }],
|
|
||||||
['display', { label: 'Display Status', type: 'select', source: [['Form', 'Show'], ['Hidden', 'Hidden']] }],
|
|
||||||
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time']] }],
|
|
||||||
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
['field_label', { label: 'Field Label', type: 'text', validation: '' }],
|
||||||
|
['field_type', { label: 'Field Type', type: 'select', source: [['text', 'Text Field'], ['textarea', 'Text Area'], ['select', 'Select'], ['select2', 'Select2'], ['select2multi', 'Multi Select'], ['fileupload', 'File Upload'], ['date', 'Date'], ['datetime', 'Date Time'], ['time', 'Time'], ['signature', 'Signature']] }],
|
||||||
['field_validation', {
|
['field_validation', {
|
||||||
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
label: 'Validation', type: 'select2', validation: 'none', sort: 'none', 'null-label': 'Required', 'allow-null': true, source: [['none', 'None'], ['number', 'Number'], ['numberOrEmpty', 'Number or Empty'], ['float', 'Decimal'], ['email', 'Email'], ['emailOrEmpty', 'Email or Empty']],
|
||||||
}],
|
}],
|
||||||
@@ -87,11 +96,14 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
],
|
],
|
||||||
validation: 'none',
|
validation: 'none',
|
||||||
}],
|
}],
|
||||||
['display_order', { label: 'Priority', type: 'text', validation: 'number' }],
|
['display_order', { label: 'Priority', type: 'text', validation: 'none' }],
|
||||||
['display_section', { label: 'Display Section', type: 'text', validation: 'none' }],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNameFromFieldName(fieldName) {
|
||||||
|
return fieldName.replace(/[^a-z0-9+]+/gi, '').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
setTableType(type) {
|
setTableType(type) {
|
||||||
this.tableType = type;
|
this.tableType = type;
|
||||||
}
|
}
|
||||||
@@ -102,18 +114,26 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
return str != null && name.test(str);
|
return str != null && name.test(str);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.currentElement == null || this.currentElement.name == null || this.currentElement.name === '') {
|
||||||
|
params.name = this.getNameFromFieldName(params.field_label);
|
||||||
|
if (!validateName(params.name)) {
|
||||||
|
return 'Invalid field label for custom field';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
params.name = this.currentElement.name;
|
||||||
|
}
|
||||||
|
|
||||||
if (!validateName(params.name)) {
|
if (!validateName(params.name)) {
|
||||||
return 'Invalid name for custom field';
|
return 'Invalid name for custom field';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
forceInjectValuesBeforeSave(params) {
|
forceInjectValuesBeforeSave(params) {
|
||||||
const data = [params.name]; const options = []; let
|
const data = ['', {}];
|
||||||
optionsData;
|
const options = [];
|
||||||
data.push({});
|
let optionsData;
|
||||||
|
|
||||||
data[1].label = params.field_label;
|
data[1].label = params.field_label;
|
||||||
data[1].type = params.field_type;
|
data[1].type = params.field_type;
|
||||||
data[1].validation = params.field_validation;
|
data[1].validation = params.field_validation;
|
||||||
@@ -128,8 +148,23 @@ class ReactCustomFieldAdapter extends AdapterBase {
|
|||||||
if (params.field_validation == null || params.field_validation === undefined) {
|
if (params.field_validation == null || params.field_validation === undefined) {
|
||||||
params.field_validation = '';
|
params.field_validation = '';
|
||||||
}
|
}
|
||||||
params.data = JSON.stringify(data);
|
|
||||||
params.type = this.tableType;
|
params.type = this.tableType;
|
||||||
|
if (this.currentElement == null || this.currentElement.name == null || this.currentElement.name === '') {
|
||||||
|
params.name = this.getNameFromFieldName(params.field_label);
|
||||||
|
} else {
|
||||||
|
params.name = this.currentElement.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = params.name;
|
||||||
|
params.data = JSON.stringify(data);
|
||||||
|
|
||||||
|
params.display = 'Form';
|
||||||
|
params.display_order = parseInt(params.display_order);
|
||||||
|
if (!Number.isInteger(params.display_order)) {
|
||||||
|
params.display_order = 1;
|
||||||
|
}
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
web/api/ReactIdNameAdapter.js
Normal file
45
web/api/ReactIdNameAdapter.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import ReactModalAdapterBase from './ReactModalAdapterBase';
|
||||||
|
|
||||||
|
class ReactIdNameAdapter extends ReactModalAdapterBase {
|
||||||
|
constructor(endPoint, tab, filter, orderBy) {
|
||||||
|
super(endPoint, tab, filter, orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataMapping() {
|
||||||
|
return [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaders() {
|
||||||
|
return [
|
||||||
|
{ sTitle: 'ID', bVisible: false },
|
||||||
|
{ sTitle: 'Name' },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormFields() {
|
||||||
|
return [
|
||||||
|
['id', { label: 'ID', type: 'hidden' }],
|
||||||
|
['name', { label: 'Name', type: 'text', validation: '' }],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableColumns() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'Id',
|
||||||
|
dataIndex: 'id',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactIdNameAdapter;
|
||||||
58
web/api/ReactLogViewAdapter.js
Normal file
58
web/api/ReactLogViewAdapter.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2018 [Glacies UG, Berlin, Germany] (http://glacies.de)
|
||||||
|
Developer: Thilina Hasantha (http://lk.linkedin.com/in/thilinah | https://github.com/thilinah)
|
||||||
|
*/
|
||||||
|
/* global timeUtils */
|
||||||
|
/**
|
||||||
|
* ReactLogViewAdapter
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ReactModalAdapterBase from './ReactModalAdapterBase';
|
||||||
|
|
||||||
|
class ReactLogViewAdapter extends ReactModalAdapterBase {
|
||||||
|
getLogs(id) {
|
||||||
|
const object = { id };
|
||||||
|
const reqJson = JSON.stringify(object);
|
||||||
|
|
||||||
|
const callBackData = [];
|
||||||
|
callBackData.callBackData = [];
|
||||||
|
callBackData.callBackSuccess = 'getLogsSuccessCallBack';
|
||||||
|
callBackData.callBackFail = 'getLogsFailCallBack';
|
||||||
|
|
||||||
|
this.customAction('getLogs', `admin=${this.modulePathName}`, reqJson, callBackData);
|
||||||
|
}
|
||||||
|
|
||||||
|
getLogsSuccessCallBack(callBackData) {
|
||||||
|
let tableLog = '<table class="table table-condensed table-bordered table-striped" style="font-size:14px;"><thead><tr><th>Notes</th></tr></thead><tbody>_days_</tbody></table> ';
|
||||||
|
const rowLog = '<tr><td><span class="logTime label label-default">_date_</span> <b>_status_</b><br/>_note_</td></tr>';
|
||||||
|
|
||||||
|
const logs = callBackData.data;
|
||||||
|
let html = '';
|
||||||
|
let rowsLogs = '';
|
||||||
|
|
||||||
|
|
||||||
|
for (let i = 0; i < logs.length; i++) {
|
||||||
|
let trow = rowLog;
|
||||||
|
trow = trow.replace(/_date_/g, logs[i].time);
|
||||||
|
trow = trow.replace(/_status_/g, `${logs[i].status_from} -> ${logs[i].status_to}`);
|
||||||
|
trow = trow.replace(/_note_/g, logs[i].note);
|
||||||
|
rowsLogs += trow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rowsLogs !== '') {
|
||||||
|
tableLog = tableLog.replace('_days_', rowsLogs);
|
||||||
|
html += tableLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showMessage('Logs', html);
|
||||||
|
|
||||||
|
timeUtils.convertToRelativeTime($('.logTime'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
getLogsFailCallBack(callBackData) {
|
||||||
|
this.showMessage('Error', 'Error occured while getting data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactLogViewAdapter;
|
||||||
@@ -48,6 +48,10 @@ class ReactModalAdapterBase extends AdapterBase {
|
|||||||
return this.access.indexOf(type) > 0;
|
return this.access.indexOf(type) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasCustomButtons() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
initTable() {
|
initTable() {
|
||||||
if (this.tableInitialized) {
|
if (this.tableInitialized) {
|
||||||
return false;
|
return false;
|
||||||
@@ -56,7 +60,11 @@ class ReactModalAdapterBase extends AdapterBase {
|
|||||||
if (tableDom) {
|
if (tableDom) {
|
||||||
this.tableContainer = React.createRef();
|
this.tableContainer = React.createRef();
|
||||||
let columns = this.getTableColumns();
|
let columns = this.getTableColumns();
|
||||||
if (this.hasAccess('save') || this.hasAccess('delete') || this.hasAccess('element')) {
|
if (this.hasAccess('save')
|
||||||
|
|| this.hasAccess('delete')
|
||||||
|
|| this.hasAccess('element')
|
||||||
|
|| this.hasCustomButtons()
|
||||||
|
) {
|
||||||
columns.push({
|
columns.push({
|
||||||
title: 'Actions',
|
title: 'Actions',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
@@ -257,6 +265,27 @@ class ReactModalAdapterBase extends AdapterBase {
|
|||||||
showLoader() {
|
showLoader() {
|
||||||
// $('#iceloader').show();
|
// $('#iceloader').show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addActualFieldsForStepModal(steps, fields) {
|
||||||
|
return steps.map((item) => {
|
||||||
|
item.fields = item.fields.reduce((acc, fieldName) => {
|
||||||
|
const field = fields.find(([name]) => name === fieldName);
|
||||||
|
if (field) {
|
||||||
|
acc.push(field);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormOptions() {
|
||||||
|
return {
|
||||||
|
width: 1024,
|
||||||
|
twoColumnLayout: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ReactModalAdapterBase;
|
export default ReactModalAdapterBase;
|
||||||
|
|||||||
70
web/components/IceColorPick.js
Normal file
70
web/components/IceColorPick.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
|
import { SketchPicker } from 'react-color';
|
||||||
|
|
||||||
|
function useComponentVisible(initialIsVisible) {
|
||||||
|
const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
const handleClickOutside = (event) => {
|
||||||
|
if (ref.current && !ref.current.contains(event.target)) {
|
||||||
|
setIsComponentVisible(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
document.addEventListener('click', handleClickOutside, true);
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('click', handleClickOutside, true);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ref, isComponentVisible, setIsComponentVisible };
|
||||||
|
}
|
||||||
|
|
||||||
|
function IceColorPick(props) {
|
||||||
|
const { value, onChange, readOnly } = props;
|
||||||
|
|
||||||
|
const { ref, isComponentVisible, setIsComponentVisible } = useComponentVisible(true);
|
||||||
|
|
||||||
|
const [color, setColor] = useState(value || '#FFF');
|
||||||
|
const [showPicker, setShowPicker] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isComponentVisible) {
|
||||||
|
setShowPicker(false);
|
||||||
|
}
|
||||||
|
}, [isComponentVisible])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (value) {
|
||||||
|
setColor(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return <div className="colorpicker-container">
|
||||||
|
<div
|
||||||
|
className="colorpicker-preview"
|
||||||
|
onClick={() => {
|
||||||
|
if (!showPicker) {
|
||||||
|
setIsComponentVisible(true);
|
||||||
|
}
|
||||||
|
setShowPicker(!showPicker);
|
||||||
|
}}
|
||||||
|
style={ { backgroundColor: color} }
|
||||||
|
/>
|
||||||
|
<div ref={ref} className={`colorpicker-component ${(readOnly || !showPicker) ? 'hidden': ''}` }>
|
||||||
|
<SketchPicker
|
||||||
|
color={color}
|
||||||
|
disableAlpha
|
||||||
|
presetColors={[]}
|
||||||
|
onChangeComplete={({ hex }) => {
|
||||||
|
onChange(hex);
|
||||||
|
setColor(hex);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IceColorPick;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user