diff --git a/plugins/configuration/core/class_DebugLevelAttribute.inc b/plugins/configuration/core/class_DebugLevelAttribute.inc new file mode 100644 index 0000000000000000000000000000000000000000..b1ac991109cf41239948ad64f61d4f6cfd742d39 --- /dev/null +++ b/plugins/configuration/core/class_DebugLevelAttribute.inc @@ -0,0 +1,48 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* Debug level is an OR combination of several values */ +class DebugLevelAttribute extends SetAttribute +{ + protected function loadAttrValue (array $attrs) + { + if (isset($attrs[$this->getLdapName()])) { + $value = $attrs[$this->getLdapName()][0]; + $this->value = []; + foreach ($this->attribute->getChoices() as $choice) { + if ($value & $choice) { + $this->value[] = $choice; + } + } + } else { + $this->resetToDefault(); + } + } + + function computeLdapValue () + { + $value = 0; + foreach ($this->value as $v) { + $value |= $v; + } + return $value; + } +} diff --git a/plugins/configuration/core/class_configInLdap.inc b/plugins/configuration/core/class_configInLdap.inc new file mode 100644 index 0000000000000000000000000000000000000000..8cf0034b45ef282dcfce54cfaf8bb38848e47d40 --- /dev/null +++ b/plugins/configuration/core/class_configInLdap.inc @@ -0,0 +1,619 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class configInLdap extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Configuration'), + 'plTitle' => _('FusionDirectory configuration'), + 'plDescription' => _('Configuration screen of FusionDirectory'), + 'plIcon' => 'geticon.php?context=categories&icon=settings&size=48', + 'plObjectClass' => ['fusionDirectoryConf'], + 'plObjectType' => [ + 'configuration' => [ + 'name' => _('FusionDirectory configuration'), + 'filter' => 'objectClass=fusionDirectoryConf', + 'tabClass' => 'configInLdapTabs', + 'icon' => 'geticon.php?context=categories&icon=settings&size=16', + 'mainAttr' => FALSE, + 'ou' => preg_replace('/^[^,]+,/', '', CONFIGRDN) + ] + ], + 'plSection' => ['conf' => ['name' => _('Configuration'), 'priority' => 20]], + 'plManages' => ['configuration'], + 'plPriority' => 0, + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + global $config; + + return [ + 'look_n_feel' => [ + 'name' => _('Look and feel'), + 'attrs' => [ + new SelectAttribute( + _('Language'), _('Language of the application. If \'automatic\' or not available, the one asked by the browser will be used. This setting can be overriden per user.'), + 'fdLanguage', FALSE, + [''] + ), + new SelectAttribute( + _('Theme'), _('Theme to be used'), + 'fdTheme', TRUE, + static::get_themes(), + 'breezy' + ), + new SelectAttribute( + _('Timezone'), _('Timezone to be used'), + 'fdTimezone', TRUE, + ['America/New_York'] + ), + new HiddenAttribute('fusionConfigMd5'), + new HiddenAttribute('fdIncrementalModifierStates'), + ] + ], + 'core_settings' => [ + 'name' => _('Core settings'), + 'attrs' => [ + new IntAttribute( + _('LDAP size limit'), _('Defines the number of entries to get from LDAP by default.'), + 'fdLdapSizeLimit', FALSE, + 0 /*min*/, FALSE /*no max*/, 200 + ), + new SelectAttribute( + _('Edit locking'), + _('Check if a entry currently being edited has been modified outside of FusionDirectory in the meantime.'), + 'fdModificationDetectionAttribute', FALSE, + ['', 'entryCSN', 'contextCSN'], 'entryCSN' + ), + new BooleanAttribute( + _('Enable logging'), + _('Event logging on FusionDirectory side.'), + 'fdLogging', FALSE, + TRUE + ), + new BooleanAttribute( + _('Schema validation'), + _('Enables schema checking during login.'), + 'fdSchemaCheck', FALSE, + TRUE + ), + new BooleanAttribute( + _('Wildcard foreign keys'), _('Enables wildcard searches like member=* when moving a whole department. This will open all existing groups and roles to make sure foreign keys are respected. Slow on big trees.'), + 'fdWildcardForeignKeys', FALSE, + TRUE + ), + ] + ], + 'password' => [ + 'name' => _('Password settings'), + 'attrs' => [ + new SetAttribute( + new SelectAttribute( + _('Allowed password hashes'), _('Password hashes which may be used for user passwords'), + 'fdPasswordAllowedHashes', TRUE, + ['ssha'] + ) + ), + new SelectAttribute( + _('Password default hash'), _('Default hash to be used'), + 'fdPasswordDefaultHash', TRUE, + ['ssha'] + ), + new BooleanAttribute( + _('Force default hash'), _('Force the use of the default password hash'), + 'fdForcePasswordDefaultHash' + ), + new IntAttribute( + _('Password minimum length'), _('Minimum length of user passwords'), + 'fdPasswordMinLength', FALSE, + 0 /*min*/, FALSE /*no max*/ + ), + new IntAttribute( + _('Password minimum differs'), _('Minimum number of different characters from last password'), + 'fdPasswordMinDiffer', FALSE, + 0 /*min*/, FALSE /*no max*/ + ), + new BooleanAttribute( + _('Use account expiration'), + _('Enables shadow attribute tests during the login to FusionDirectory and forces password renewal or account locking'), + 'fdHandleExpiredAccounts' + ), + new StringAttribute( + _('SASL Realm'), _('SASL Realm'), + 'fdSaslRealm' + ), + new StringAttribute( + _('SASL Exop'), _('Attribute to be stored in the userPassword attribute'), + 'fdSaslExop' + ), + ] + ], + 'login' => [ + 'name' => _('Login and session'), + 'attrs' => [ + new SelectAttribute( + _('Login attribute'), + _('Which LDAP attribute should be used as the login name during login.'), + 'fdLoginAttribute', TRUE, + ['uid', 'mail', 'uid,mail'], 'uid', + ['uid', 'mail', 'both'] + ), + new BooleanAttribute( + _('Enforce encrypted connections'), + _('Enables PHP security checks to force encrypted access (https) to the web interface.'), + 'fdForceSSL' + ), + new BooleanAttribute( + _('Warn if session is not encrypted'), + _('will display a warning to the user when http is used instead of https.'), + 'fdWarnSSL', FALSE, + TRUE + ), + new IntAttribute( + _('Session lifetime'), _('Defines when a session will expire in seconds (0 to disable).'), + 'fdSessionLifeTime', TRUE, + 0 /*min*/, FALSE /*no max*/, 1800 + ), + new SelectAttribute( + _('Login method'), + _('Which login method should be used for connecting to FusionDirectory'), + 'fdLoginMethod', TRUE + ), + new StringAttribute( + _('Header name'), _('Name of the header containing user identifier.'), + 'fdHttpHeaderAuthHeaderName', FALSE, + 'AUTH_USER' + ), + ] + ], + 'ssl' => [ + 'name' => _('SSL'), + 'attrs' => [ + new TrimmedStringAttribute( + _('Key path'), _('Path to FusionDirectory private key. Unused for now.'), + 'fdSslKeyPath', FALSE, + '/etc/ssl/private/fd.key' + ), + new TrimmedStringAttribute( + _('Certificate path'), _('Path to FusionDirectory certificate. Unused for now.'), + 'fdSslCertPath', FALSE, + '/etc/ssl/certs/fd.cert' + ), + new TrimmedStringAttribute( + _('CA certificate path'), _('Path to the CA certificate. Used for validating Argonaut Server host.'), + 'fdSslCaCertPath', FALSE, + '/etc/ssl/certs/ca.cert' + ), + ] + ], + 'cas' => [ + 'name' => _('CAS'), + 'attrs' => [ + new TrimmedStringAttribute( + _('CA certificate path'), _('Path to the CA certificate of the CAS server'), + 'fdCasServerCaCertPath', FALSE, + '/etc/ssl/certs/ca.cert' + ), + new StringAttribute( + _('Host'), _('Host of the CAS server'), + 'fdCasHost', FALSE, + 'localhost' + ), + new IntAttribute( + _('Port'), _('Port the CAS server is listening on'), + 'fdCasPort', FALSE, + 0 /*min*/, FALSE /*no max*/, 443 + ), + new StringAttribute( + _('CAS context'), _('CAS context to be used'), + 'fdCasContext', FALSE, + '/cas' + ), + new BooleanAttribute( + _('Verbose error'), _('Activate verbose errors in phpCAS. Avoid in production.'), + 'fdCasVerbose', FALSE + ), + new BooleanAttribute( + _('Library CAS 1.6'), _('Activate if library CAS >= 1.6 is being used.'), + 'fdCasLibraryBool', FALSE + ), + new StringAttribute( + _('Client service'), _('The client service name'), + 'fdCasClientServiceName', FALSE + ), + ] + ], + 'people_and_group' => [ + 'name' => _('People and group storage'), + 'class' => ['critical'], + 'attrs' => [ + new SelectAttribute( + _('People DN attribute'), _('Attribute to use at the beginning of the user dn'), + 'fdAccountPrimaryAttribute', TRUE, + ['uid', 'cn'] + ), + new StringAttribute( + _('CN pattern'), _('The pattern to use to build the common name field'), + 'fdCnPattern', TRUE, + '%givenName% %sn%' + ), + new BooleanAttribute( + _('Mandatory first name'), + _('Whether first name (givenName) should be a mandatory field on users'), + 'fdGivenNameRequired', FALSE, + TRUE + ), + new BooleanAttribute( + _('Strict naming policy'), + _('Enables strict checking of user and group names'), + 'fdStrictNamingRules', FALSE, + TRUE + ), + new StringAttribute( + _('Users RDN'), _('The branch where users are stored.'), + 'fdUserRDN', TRUE, + 'ou=people' + ), + new StringAttribute( + _('ACL role RDN'), _('The branch where ACL roles are stored.'), + 'fdAclRoleRDN', TRUE, + 'ou=aclroles' + ), + new BooleanAttribute( + _('Restrict role members'), _('When enabled only users from the same branch or members of groups from the same branch can be added to a role.'), + 'fdRestrictRoleMembers' + ), + new BooleanAttribute( + _('Separate address fields'), _('Expose street, postOfficeBox and postalCode fields instead of postalAddress.'), + 'fdSplitPostalAddress' + ), + new PostalAddressAttribute( + _('Postal address pattern'), _('When using separate address fields, you can use a pattern to fill postalAddress field.'), + 'fdPostalAddressPattern' + ), + new IntAttribute( + _('Avatar max size'), _('Maximum user picture width and height in pixels. Bigger uploaded pictures will be resized.'), + 'fdMaxAvatarSize', FALSE, + 1, FALSE, 200 + ), + ] + ], + 'debug' => [ + 'name' => _('Debugging'), + 'attrs' => [ + new BooleanAttribute( + _('Display PHP errors'), + _('Shows PHP errors in the upper part of the screen. This should be disabled in production deployments, because it may contain passwords.'), + 'fdDisplayErrors' + ), + new IntAttribute( + _('Maximum LDAP query time'), _('Stop LDAP actions if there is no answer within the specified number of seconds.'), + 'fdLdapMaxQueryTime', FALSE, + 0 /*min*/, FALSE /*no max*/ + ), + new BooleanAttribute( + _('Log LDAP statistics'), + _('Track LDAP timing statistics to the syslog. This may help to find indexing problems or bad search filters.'), + 'fdLdapStats' + ), + new DebugLevelAttribute( + new SelectAttribute( + _('Debug level'), + _('Display certain information on each page load.'), + 'fdDebugLevel', FALSE, + [DEBUG_TRACE, DEBUG_LDAP, DEBUG_DB, DEBUG_SHELL, DEBUG_POST, + DEBUG_SESSION, DEBUG_ACL, DEBUG_SI, DEBUG_MAIL], + DEBUG_TRACE, + ['Trace', 'LDAP', 'Database', 'Shell', 'POST', + 'SESSION', 'ACL', 'SI', 'Mail'] + ) + ), + new BooleanAttribute( + _('Log debug messages'), + _('Sends debug output to syslog as well'), + 'fdDebugLogging' + ), + ] + ], + 'miscellaneous' => [ + 'name' => _('Miscellaneous'), + 'attrs' => [ + new BooleanAttribute( + _('Display summary in listings'), + _('Determines whether a status bar will be shown on the bottom of lists, displaying a short summary of type and number of elements in the list.'), + 'fdListSummary', FALSE, + TRUE + ), + new BooleanAttribute( + _('Show ACL tab on all objects'), + _('For very specific ACL rights setting where you might need to give right on a single object.'), + 'fdAclTabOnObjects' + ), + new SetAttribute( + new StringAttribute( + _('Available department categories'), _('Available categories in the departments dropdown'), + 'fdDepartmentCategories', FALSE + ), + [] + ), + new OrderedArrayAttribute( + new PipeSeparatedCompositeAttribute( + _('Use this to hide some menu entry to specific groups of users'), + 'fdPluginsMenuBlacklist', + [ + new SelectAttribute( + '', _('Group or role'), + 'blacklistGroup', TRUE, + [] + ), + new SelectAttribute( + '', _('Plugin to blacklist'), + 'blacklistPlugin', TRUE, + [] + ), + ], + '', + _('Plugin menu blacklist') + ), + // no order + FALSE, + [] + ), + // Needed here for ACLs + new HiddenAttribute('fdManagementConfig'), + new IntAttribute( + _('ACL target filter limit'), _('Defines the maximum number of entries an ACL target filter is allowed to return'), + 'fdAclTargetFilterLimit', FALSE, + 0 /*min*/, FALSE /*no max*/, 100 + ), + ] + ], + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE, $attributesInfo = NULL) + { + global $config; + $attributesInfo = static::getAttributesInfo(); + /* Languages */ + $languages = Language::getList(TRUE); + asort($languages); + $languages = array_merge(["" => _("Automatic")], $languages); + $attributesInfo['look_n_feel']['attrs'][0]->setChoices(array_keys($languages), array_values($languages)); + /* Timezones */ + $attributesInfo['look_n_feel']['attrs'][2]->setChoices(timezone::_get_tz_zones()); + /* Password methods */ + $methods = passwordMethod::get_available_methods(); + $methods = $methods['name']; + if (!in_array('sasl', $methods)) { + $methods[] = 'sasl'; + } + $attributesInfo['password']['attrs'][0]->attribute->setChoices($methods); + $attributesInfo['password']['attrs'][0]->setDefaultValue($methods); + $attributesInfo['password']['attrs'][0]->resetToDefault(); + $attributesInfo['password']['attrs'][1]->setChoices($methods); + /* Login methods */ + $methods = LoginMethod::getMethods(); + $attributesInfo['login']['attrs'][4]->setChoices(array_keys($methods), array_values($methods)); + + $groupsAndRoles = array_merge( + array_map( + function ($group) + { + return sprintf(_('Group %s'), $group); + }, + objects::ls('ogroup') + ), + array_map( + function ($role) + { + return sprintf(_('Role %s'), $role); + }, + objects::ls('role') + ) + ); + $attributesInfo['miscellaneous']['attrs'][3]->attribute->attributes[0]->setChoices( + array_keys($groupsAndRoles), + array_values($groupsAndRoles) + ); + $menuPlugins = []; + $plist = session::get('plist'); + foreach ($config->data['SECTIONS'] as $section => $section_infos) { + foreach ($config->data['MENU'][$section] as $info) { + if (isset($info['CLASS'])) { + list ($plHeadline, , , ) = $plist->get_infos($info['CLASS']); + $menuPlugins[$info['CLASS']] = $plHeadline; + } + } + } + asort($menuPlugins); + $attributesInfo['miscellaneous']['attrs'][3]->attribute->attributes[1]->setChoices( + array_keys($menuPlugins), + array_values($menuPlugins) + ); + + try { + parent::__construct($dn, $object, $parent, $mainTab, $attributesInfo); + } catch (NonExistingLdapNodeException $e) { + parent::__construct('new', $object, $parent, $mainTab, $attributesInfo); + $this->dn = $dn; + } + + $this->fusionConfigMd5 = md5_file(CACHE_DIR."/".CLASS_CACHE); + + $this->attributesAccess['fdForceSSL']->setManagedAttributes( + [ + 'disable' => [ + TRUE => [ + 'fdWarnSSL', + ] + ] + ] + ); + $this->attributesAccess['fdSplitPostalAddress']->setManagedAttributes( + [ + 'disable' => [ + FALSE => [ + 'fdPostalAddressPattern', + ] + ] + ] + ); + + // CAS boolean case to allow the use of CAS library >= 1.6 + $this->attributesAccess['fdCasLibraryBool']->setManagedAttributes( + [ + 'disable' => [ + FALSE => [ + 'fdCasClientServiceName', + ] + ] + ] + ); + + $this->attributesAccess['fdLoginMethod']->setManagedAttributes( + [ + 'multiplevalues' => [ + 'noncas' => [ + 'LoginPost', + 'LoginHTTPAuth', + 'LoginHTTPHeader', + ], + 'nonheader' => [ + 'LoginPost', + 'LoginCAS', + 'LoginHTTPAuth' + ], + ], + 'disable' => [ + 'noncas' => [ + 'fdCasServerCaCertPath', + 'fdCasHost', + 'fdCasPort', + 'fdCasContext', + 'fdCasVerbose', + 'fdCasClientServiceName', + 'fdCasLibraryBool' + ], + 'nonheader' => [ + 'fdHttpHeaderAuthHeaderName', + ] + ] + ] + ); + if (empty($this->attrs['fdLoginMethod'][0])) { + // Reading OBSOLETEÂ attributes from FD<1.4 to ease migration + if (isset($this->attrs['fdHttpAuthActivated'][0]) && ($this->attrs['fdHttpAuthActivated'][0] == 'TRUE')) { + $this->fdLoginMethod = 'LoginHTTPAuth'; + } elseif (isset($this->attrs['fdCasActivated'][0]) && ($this->attrs['fdCasActivated'][0] == 'TRUE')) { + $this->fdLoginMethod = 'LoginCAS'; + } elseif (isset($this->attrs['fdHttpHeaderAuthActivated'][0]) && ($this->attrs['fdHttpHeaderAuthActivated'][0] == 'TRUE')) { + $this->fdLoginMethod = 'LoginHTTPHeader'; + } + } + + $this->attributesAccess['fdPasswordDefaultHash']->setChoices( + $this->attributesAccess['fdPasswordAllowedHashes']->getValue() + ); + } + + function compute_dn (): string + { + return $this->dn; + } + + function check (): array + { + $messages = parent::check(); + if (($this->fdPasswordDefaultHash == 'sasl') && ($this->fdSaslRealm == '') && ($this->fdSaslExop == '')) { + $messages[] = new SimplePluginCheckError( + $this, + htmlescape(_('You need to fill saslRealm or saslExop in the configuration screen in order to use SASL')) + ); + } + if ($this->attributesAccess['fdLanguage']->hasChanged() && ($this->fdLanguage != '') && !Language::isAvailable($this->fdLanguage)) { + $messages[] = new SimplePluginCheckError( + $this->attributesAccess['fdLanguage'], + htmlescape(sprintf(_('It seems the selected language "%s" is not installed on the system. Please install it or select an other one.'), $this->fdLanguage)) + ); + } + + if (($this->fdLdapSizeLimit !== '') && ($this->fdLdapSizeLimit > 0)) { + $error = ldapSizeLimit::checkMaxInputVars($this->fdLdapSizeLimit); + if ($error !== FALSE) { + $messages[] = new SimplePluginCheckError( + $this->attributesAccess['fdLdapSizeLimit'], + $error->getHtmlMessage(), + $error->getCode(), + $error + ); + } + } + + return $messages; + } + + public function update (): bool + { + $res = parent::update(); + + $this->attributesAccess['fdPasswordDefaultHash']->setChoices( + $this->attributesAccess['fdPasswordAllowedHashes']->getValue() + ); + + return $res; + } + + static function get_themes () + { + $themesdir = '../ihtml/themes/'; + $themes = array_keys(session::get(IconTheme::$session_var)); + if ($dir = opendir("$themesdir")) { + while (($file = readdir($dir)) !== FALSE) { + if (is_dir("$themesdir/$file") && !preg_match("/^\./", $file)) { + $themes[] = $file; + } + } + } + return array_unique($themes); + } + + static function mainInc ($classname = NULL, $entry_dn = NULL, $tabs = TRUE, $edit_mode = TRUE, $objectType = FALSE) + { + global $config; + + if ($classname === NULL) { + $classname = get_called_class(); + } + + if ($entry_dn === NULL) { + $entry_dn = CONFIGRDN.$config->current['BASE']; + } + + parent::mainInc($classname, $entry_dn, $tabs, $edit_mode, $objectType); + } +} diff --git a/plugins/configuration/core/class_configInLdapTabs.inc b/plugins/configuration/core/class_configInLdapTabs.inc new file mode 100644 index 0000000000000000000000000000000000000000..243e0dcaacdf1a399bffc173fca5c7dfd5cfee97 --- /dev/null +++ b/plugins/configuration/core/class_configInLdapTabs.inc @@ -0,0 +1,37 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class configInLdapTabs extends simpleTabs +{ + function save () + { + global $config; + $errors = parent::save(); + /* check_and_reload */ + $config->check_and_reload(TRUE); + /* reload plist as well as some plInfo might use $config or get_ou */ + session::un_set('plist'); + pluglist::load(); + session::un_set('passwordMethod::get_available_methods'); + + return $errors; + } +} diff --git a/plugins/configuration/core/class_hooksConfig.inc b/plugins/configuration/core/class_hooksConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..759543f7894e6845605a10f1a9070cd503da1d77 --- /dev/null +++ b/plugins/configuration/core/class_hooksConfig.inc @@ -0,0 +1,113 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class hooksConfig extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Triggers'), + 'plTitle' => _('Triggers configuration'), + 'plPriority' => 1, + 'plObjectType' => ['configuration'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + global $config; + + $plugins = []; + if (session::is_set('plist')) { + $plugins = array_keys(session::get('plist')->info); + } + sort($plugins); + + return [ + 'triggers' => [ + 'name' => _('Triggers'), + 'class' => ['fullwidth'], + 'attrs' => [ + new BooleanAttribute( + _('Display trigger output'), + _('When enabled successful trigger execution output is displayed to the user using a dialog.'), + 'fdDisplayHookOutput' + ), + new OrderedArrayAttribute( + new CompositeAttribute( + _('Triggers that are called when specific actions happens'), + 'fdTabHook', + [ + new SelectAttribute( + _('Tab'), _('The tab that this trigger concerns'), + 'triggerTab', TRUE, + $plugins + ), + new SelectAttribute( + _('Mode'), _('When to call this command'), + 'triggerMode', TRUE, + ['postcreate', 'postremove', 'postmodify', + 'precreate', 'preremove', 'premodify', 'check', + 'prelock', 'postlock', 'preunlock', 'postunlock'] + ), + new BooleanAttribute( + _('Deactivated'), _('Use this to temporarily deactivate a trigger'), + 'triggerDecativate', TRUE, + FALSE, '', + '#', '' + ), + new TextAreaAttribute( + _('Command'), _('The command that will be called'), + 'triggerCmd', TRUE + ) + ], + '/^([^\\|]+)\\|([^\\|]+)\\|(#?)(.*)$/', + '%s|%s|%s%s', + '', // acl + _('Triggers') + ), + FALSE, // non-ordered + [], + TRUE // edition + ), + ] + ], + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['fdTabHook']->setLinearRendering(FALSE); + $this->attributesAccess['fdTabHook']->setHeaders([ + _('Tab'), + _('Mode'), + _('Deactivated'), + _('Command'), + '', + ]); + } +} diff --git a/plugins/configuration/core/class_licenceInfo.inc b/plugins/configuration/core/class_licenceInfo.inc new file mode 100644 index 0000000000000000000000000000000000000000..81e4b8ffbe90b635a75dcb00e9807c0fdedea731 --- /dev/null +++ b/plugins/configuration/core/class_licenceInfo.inc @@ -0,0 +1,240 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2020-2021 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class licenceInfo extends simplePlugin +{ + /* ldap attribute => ini variable */ + public static $subscriptionAttributes = [ + 'fdSubscriptionName' => 'Name', + 'uid' => 'Id', + 'fdSubscriptionType' => 'Type', + 'fdSubscriptionContractId' => 'Contract', + 'fdSubscriptionStartDate' => 'StartDate', + 'fdSubscriptionEndDate' => 'EndDate', + ]; + + public static $sectionName = 'FusionDirectory Subscription'; + + public static function plInfo (): array + { + return [ + 'plShortName' => _('Subscription'), + 'plTitle' => _('Subscription information'), + 'plDescription' => _('Information about your FusionDirectory subscription'), + 'plIcon' => 'geticon.php?context=applications&icon=fusiondirectory&size=48', + 'plObjectClass' => ['fdSubscriptionInformation'], + 'plObjectType' => [ + 'subscriptionInfo' => [ + 'name' => _('Subscription information'), + 'ou' => get_ou('fusiondirectoryRDN'), + ] + ], + 'plSection' => 'conf', + 'plPriority' => 1, + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()), + ]; + } + + public static function getAttributesInfo (): array + { + return [ + 'info' => [ + 'name' => _('Subscription information'), + 'attrs' => [ + new HiddenAttribute( + 'cn', TRUE, 'subscription' + ), + new DisplayAttribute( + '', _('Information text for subscription'), + 'fdSubscriptionText', FALSE + ), + new DisplayLDAPAttribute( + _('Name'), _('Subscription name'), + 'fdSubscriptionName', TRUE + ), + new DisplayLDAPAttribute( + _('Id'), _('Subscription id'), + 'uid', TRUE + ), + new DisplayLDAPAttribute( + _('Type'), _('Subscription type'), + 'fdSubscriptionType', TRUE + ), + new DisplayLDAPAttribute( + _('Contract'), _('Contract reference'), + 'fdSubscriptionContractId', TRUE + ), + new GeneralizedTimeDisplayAttribute( + _('Start date'), _('Start date of this subscription'), + 'fdSubscriptionStartDate', TRUE, + '', 'Y-m-d' + ), + new GeneralizedTimeDisplayAttribute( + _('End date'), _('End date of this subscription'), + 'fdSubscriptionEndDate', TRUE, + '', 'Y-m-d' + ), + ], + ], + 'import' => [ + 'name' => _('Import your subscription'), + 'attrs' => [ + new DisplayAttribute( + '', _('Information text for expired subscription'), + 'fdSubscriptionTextExpired', FALSE + ), + new FileAttribute( + '', _('Import subscription'), + 'import_file', FALSE + ), + new ButtonAttribute( + '', '', + 'import', + _('Import'), + NULL, '', + 'import_file' + ), + ], + ], + ]; + } + + public function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['import_file']->setInLdap(FALSE); + $this->attributesAccess['import']->setInLdap(FALSE); + + $this->fdSubscriptionText = '<p>'.nl2br(sprintf( + htmlescape(_("You do not have a valid subscription for this instance of FusionDirectory.\nPlease visit %s for a list of available options.")), + '<a href="https://www.fusiondirectory.org/support/" target="_blank"><strong>https://www.fusiondirectory.org/support/</strong></a>' + )).'</p>'; + $this->attributesAccess['fdSubscriptionText']->setAllowHTML(TRUE); + $this->fdSubscriptionTextExpired = '<p>'.nl2br(sprintf( + htmlescape(_("Your subscription is expired for this instance of FusionDirectory.\nPlease visit %s to renew it.")), + '<a href="https://www.fusiondirectory.org/support/" target="_blank"><strong>https://www.fusiondirectory.org/support/</strong></a>' + )).'</p>'; + $this->attributesAccess['fdSubscriptionTextExpired']->setAllowHTML(TRUE); + } + + public function update (): bool + { + parent::update(); + + if ($this->uid !== '') { + $this->attributesAccess['fdSubscriptionText']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionName']->setVisible(TRUE); + $this->attributesAccess['uid']->setVisible(TRUE); + $this->attributesAccess['fdSubscriptionType']->setVisible(TRUE); + $this->attributesAccess['fdSubscriptionContractId']->setVisible(TRUE); + $this->attributesAccess['fdSubscriptionStartDate']->setVisible(TRUE); + $this->attributesAccess['fdSubscriptionEndDate']->setVisible(TRUE); + if ($this->attributesAccess['fdSubscriptionEndDate']->getDateValue() < new DateTime()) { + $this->attributesAccess['fdSubscriptionTextExpired']->setVisible(TRUE); + } else { + $this->attributesAccess['fdSubscriptionTextExpired']->setVisible(FALSE); + } + } else { + $this->attributesAccess['fdSubscriptionText']->setVisible(TRUE); + $this->attributesAccess['fdSubscriptionName']->setVisible(FALSE); + $this->attributesAccess['uid']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionType']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionContractId']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionStartDate']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionEndDate']->setVisible(FALSE); + $this->attributesAccess['fdSubscriptionTextExpired']->setVisible(FALSE); + } + + return TRUE; + } + + public function handle_import () + { + $data = $this->import_file; + if (empty($data)) { + /* No file or empty file */ + $error = new SimplePluginError( + $this->attributesAccess['import_file'], + htmlescape(_('No data. Did you forgot to upload a file?')) + ); + $error->display(); + } elseif (($data = parse_ini_string($data, TRUE)) === FALSE) { + /* Import of INI failed */ + $error = new SimplePluginError( + $this->attributesAccess['import_file'], + htmlescape(_('Failed to parse imported file')) + ); + $error->display(); + } else { + /* Check content of ini file */ + $errorText = NULL; + if (!isset($data[static::$sectionName])) { + $errorText = sprintf(_('Missing section "%s" in imported file'), static::$sectionName); + } else { + $data = $data[static::$sectionName]; + foreach (static::$subscriptionAttributes as $attr => $iniVar) { + if ($this->attributesAccess[$attr]->isRequired() && (!isset($data[$iniVar]) || ($data[$iniVar] === ''))) { + $errorText = sprintf(_('Missing attribute "%s" in imported file'), $iniVar); + break; + } + } + } + if ($errorText !== NULL) { + $error = new SimplePluginError( + $this->attributesAccess['import_file'], + htmlescape($errorText) + ); + $error->display(); + } else { + /* Import data and save it to the LDAP */ + foreach (static::$subscriptionAttributes as $attr => $iniVar) { + $this->attributesAccess[$attr]->setValue($data[$iniVar] ?? ''); + } + $errors = $this->parent->save(); + if (count($errors) > 0) { + msg_dialog::displayChecks($errors); + /* Reset fields to LDAP value */ + foreach (array_keys(static::$subscriptionAttributes) as $attr) { + $this->attributesAccess[$attr]->setValue($this->attributesAccess[$attr]->getInitialValue()); + } + } else { + /* The object will stay open so it needs to appear as loaded from LDAP */ + $this->is_account = TRUE; + $this->initially_was_account = TRUE; + } + } + } + /* Avoid double import */ + $this->import_file = ''; + } + + public static function mainInc ($classname = NULL, $entry_dn = NULL, $tabs = FALSE, $edit_mode = FALSE, $objectType = FALSE) + { + global $config; + + if ($entry_dn === NULL) { + $entry_dn = 'cn=subscription,'.get_ou('fusiondirectoryRDN').$config->current['BASE']; + } + + parent::mainInc($classname, $entry_dn, $tabs, $edit_mode, $objectType); + } +} diff --git a/plugins/configuration/core/class_mailTemplateConfig.inc b/plugins/configuration/core/class_mailTemplateConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..f8e8f4cd014bbfffd137d3c0b9356ae9b8fe84cb --- /dev/null +++ b/plugins/configuration/core/class_mailTemplateConfig.inc @@ -0,0 +1,52 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2012-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class mailTemplateConfig extends multiPluginSection +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('mail template configuration'), + 'plDescription' => _('FusionDirectory mail template plugin configuration'), + 'plObjectClass' => ['fdMailTemplateConf'], + 'plCategory' => ['configuration'], + 'plObjectType' => ['smallConfig'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'mailTemplateConf' => [ + 'name' => _('Mail Template'), + 'attrs' => [ + new StringAttribute( + _('Mail Template RDN'), _('Branch in which mails templates will be stored'), + 'fdMailTemplateRDN', TRUE, + 'ou=mailTemplate' + ), + ] + ], + ]; + } +} + diff --git a/plugins/configuration/core/class_mainPluginsConfig.inc b/plugins/configuration/core/class_mainPluginsConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..47169515c6c1565cc087c260e16dfdb36a3af212 --- /dev/null +++ b/plugins/configuration/core/class_mainPluginsConfig.inc @@ -0,0 +1,62 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2012-2017 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class mainPluginsConfig extends multiPluginSection +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Plugins configuration'), + 'plDescription' => _('FusionDirectory plugins configuration'), + 'plCategory' => ['configuration'], + 'plObjectClass' => ['fusionDirectoryPluginsConf'], + 'plObjectType' => ['smallConfig'], + 'plPriority' => 1, + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'ogroups' => [ + 'name' => _('Object groups'), + 'attrs' => [ + new StringAttribute( + _('Groups RDN'), _('Branch in which object groups will be stored'), + 'fdOGroupRDN', TRUE, + 'ou=groups' + ), + ] + ], + 'sasl' => [ + 'name' => _('SASL'), + 'attrs' => [ + new BooleanAttribute( + _('Force asking for a password'), _('Useful if you add a trigger using password value when SASL user passwords are edited'), + 'fdForceSaslPasswordAsk', TRUE, + FALSE + ), + ] + ], + ]; + } +} diff --git a/plugins/configuration/core/class_pluginsConfigInLdap.inc b/plugins/configuration/core/class_pluginsConfigInLdap.inc new file mode 100644 index 0000000000000000000000000000000000000000..1fb9ea1f9b9f51225a53365ed8f52ffaaee32b40 --- /dev/null +++ b/plugins/configuration/core/class_pluginsConfigInLdap.inc @@ -0,0 +1,40 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2012-2018 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class pluginsConfigInLdap extends multiPlugin +{ + protected static $tabClass = 'SMALLCONFIGTABS'; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Plugins'), + 'plDescription' => _('Configuration for plugins'), + 'plPriority' => 2, + 'plObjectType' => ['configuration'], + 'plSubTabs' => 'SMALLCONFIGTABS', + + /* This is incomplete because of dynamic loading, but is enough to generate a filter for this tab */ + 'plObjectClass' => ['fusionDirectoryPluginsConf'], + + 'plProvidedAcls' => [] + ]; + } +} diff --git a/plugins/configuration/core/class_recoveryConfig.inc b/plugins/configuration/core/class_recoveryConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..aa0303f25a8f273dcb0d610cd969265fc15b431b --- /dev/null +++ b/plugins/configuration/core/class_recoveryConfig.inc @@ -0,0 +1,157 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class recoveryConfig extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Recovery'), + 'plTitle' => _('Password recovery'), + 'plDescription' => _('Settings for the password recovery feature'), + 'plObjectClass' => ['fdPasswordRecoveryConf'], + 'plPriority' => 14, + 'plObjectType' => ['configuration'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'settings' => [ + 'name' => _('Password recovery settings'), + 'class' => ['alone'], + 'template' => get_template_path('simpleplugin_section_alone.tpl'), + 'attrs' => [ + new BooleanAttribute( + _('Activate password recovery'), + _('Whether to activate the password recovery feature or not'), + 'fdPasswordRecoveryActivated', + FALSE + ), + new MailAttribute( + _('Sender email address'), + _('Email address from which mails will be sent'), + 'fdPasswordRecoveryEmail', + TRUE, + 'to.be@chang.ed' + ), + new IntAttribute( + _('Link validity (minutes)'), + _('Number of minutes before a recovery link expires'), + 'fdPasswordRecoveryValidity', + TRUE, + 0, FALSE, 10 + ), + new StringAttribute( + _('Salt for tokens'), + _('Just a security measure, you can put anything in there, even random characters'), + 'fdPasswordRecoverySalt', + TRUE, + "SomethingSecretAndVeryLong" + ), + new BooleanAttribute( + _('Allow the use of alternate addresses'), + _('Users will also be able to enter one of their alternate addresses to recover their password'), + 'fdPasswordRecoveryUseAlternate', + FALSE + ), + new StringAttribute( + _('Login attribute'), _('Usually uid, but you can use something else for instance in case of SSO'), + 'fdPasswordRecoveryLoginAttribute', TRUE, + 'uid' + ), + ] + ], + 'first_email' => [ + 'name' => _('First email'), + 'attrs' => [ + new StringAttribute( + _('Subject'), + _('Subject of the first email'), + 'fdPasswordRecoveryMailSubject', + TRUE, + _("[FusionDirectory] Password recovery link") + ), + new TextAreaAttribute( + _('Body (first %s is login, second is link)'), + _('Body of the first email, sent when the user ask for a new password. Use %s for the login and the recovery link.'), + 'fdPasswordRecoveryMailBody', + TRUE, + _("Hello,\n\nHere is your information: \n - Login : %s\n - Link : %s\n\nThis link is only valid for 10 minutes.") + ) + ] + ], + 'second_email' => [ + 'name' => _('Second email'), + 'attrs' => [ + new StringAttribute( + _('Subject'), + _('Subject of the second email'), + 'fdPasswordRecoveryMail2Subject', + TRUE, + _("[FusionDirectory] Password recovery successful") + ), + new TextAreaAttribute( + _('Body (%s is login)'), + _('Body of the second email, sent to confirm the password has been changed. Use %s for the user login.'), + 'fdPasswordRecoveryMail2Body', + TRUE, + _("Hello,\n\nYour password has been changed.\nYour login is still %s.") + ) + ] + ] + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + // Check if token branch is here + $ldap = $config->get_ldap_link(); + $tokenBranch = get_ou('recoveryTokenRDN').get_ou('fusiondirectoryRDN').$config->current['BASE']; + $ldap->cat($tokenBranch, ['dn']); + if (!$ldap->count()) { + $ldap->cd($config->current['BASE']); + try { + $ldap->create_missing_trees($tokenBranch); + } catch (FusionDirectoryError $error) { + $error->display(); + } + fusiondirectory_log('Created token branch '.$tokenBranch); + } + + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['fdPasswordRecoveryActivated']->setManagedAttributes( + [ + 'disable' => [ + FALSE => [ + 'fdPasswordRecoveryEmail','fdPasswordRecoveryValidity','fdPasswordRecoverySalt', + 'fdPasswordRecoveryMailSubject','fdPasswordRecoveryMailBody', + 'fdPasswordRecoveryMail2Subject','fdPasswordRecoveryMail2Body' + ] + ] + ] + ); + } +} diff --git a/plugins/configuration/core/class_snapshotConfig.inc b/plugins/configuration/core/class_snapshotConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..8c260e08228c0982cba47d0f110a921d1a31bf7d --- /dev/null +++ b/plugins/configuration/core/class_snapshotConfig.inc @@ -0,0 +1,106 @@ +<?php +/* +This code is part of FusionDirectory (http://www.fusiondirectory.org/) +Copyright (C) 2012-2023 FusionDirectory + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class snapshotsConfig extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Snapshots'), + 'plDescription' => _('FusionDirectory Snapshot Configuration'), + 'plObjectClass' => ['fusionDirectoryConf'], + 'plObjectType' => ['configuration'], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + global $config; + + return [ + 'snapshotsConf' => [ + 'name' => _('Snapshots Configuration'), + 'attrs' => [ + new BooleanAttribute( + _('Enable snapshots'), _('This enables you to save certain states of entries and restore them later on.'), + 'fdEnableSnapshots', FALSE, + TRUE + ), + new BooleanAttribute( + _('Enable automatic snapshots'), _('This enables you to automatically create a snapshot upon saving if any modifications have been found.'), + 'fdEnableAutomaticSnapshots', FALSE, + FALSE + ), + new StringAttribute( + _('Snapshot base'), _('The base where snapshots should be stored inside the LDAP directory.'), + 'fdSnapshotBase', FALSE, + 'ou=snapshots,'.$config->current['BASE'] + ), + ] + ], + 'snapshotsAdvanceConf' => [ + 'name' => _('Snapshots Advance Configuration'), + 'attrs' => [ + new IntAttribute( + _('Minimum number of snapshots to be kept'), _('Set the minimum number of snapshots to be kept'), + 'fdSnapshotMinRetention', FALSE, '', FALSE, '' + ), + new IntAttribute( + _('Retention time in days'), _('Set the retention time in days for a snapshots to be kept'), + 'fdSnapshotRetentionDays', FALSE, '', FALSE, '' + ), + ] + ], + 'OriginDataSource' => [ + 'name' => _('List of available sources / origin of data'), + 'attrs' => [ + new SetAttribute( + new StringAttribute( + _('Origin / source of data'), _('Origin / Source of data'), + 'fdSnapshotSourceData', FALSE, + ) + ), + ] + ], + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['fdEnableSnapshots']->setManagedAttributes( + [ + 'disable' => [ + FALSE => [ + 'fdSnapshotBase', + 'fdEnableAutomaticSnapshots', + 'fdSnapshotMinRetention', + 'fdSnapshotRetentionDays', + ] + ] + ] + ); + } + +} + diff --git a/plugins/configuration/core/class_tasksConfig.inc b/plugins/configuration/core/class_tasksConfig.inc new file mode 100644 index 0000000000000000000000000000000000000000..7a35d5621be8cb7bee2feb1ba040e5aec424688b --- /dev/null +++ b/plugins/configuration/core/class_tasksConfig.inc @@ -0,0 +1,78 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2012-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class tasksConfig extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Tasks'), + 'plDescription' => _('FusionDirectory Tasks Configuration'), + 'plObjectClass' => ['fdTasksConf'], + 'plObjectType' => ['configuration'], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'tasksConf' => [ + 'name' => _('Tasks Configuration'), + 'attrs' => [ + new StringAttribute( + _('Tasks RDN'), _('Branch in which Tasks will be stored'), + 'fdTasksRDN', TRUE, + 'ou=tasks' + ), + ] + ], + 'Mail' => [ + 'name' => _('Mail Anti-Spam Configuration'), + 'attrs' => [ + new IntAttribute( + _('Last Sent Mail'), '_(Last timestamp in Unix format when last email was sent succesfully)', + 'fdTasksConfLastExecTime', FALSE, '', FALSE, '' + ), + new IntAttribute( + _('Maximum number of e-mails processed per time'), _('Select max emails to be proccessed'), + 'fdTasksConfMaxEmails', FALSE, '', FALSE, '' + ), + new IntAttribute( + _('Time interval between e-mails processing (Minutes) '), _('Select min interval time between emails processing'), + 'fdTasksConfIntervalEmails', FALSE, '', FALSE, '' + ), + ] + ], + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + parent::__construct($dn, $object, $parent, $mainTab); + + // This attribute will be updated and verified by Orchestrator + $this->attributesAccess['fdTasksConfLastExecTime']->setVisible(FALSE); + + } + +} + diff --git a/plugins/management/acl/class_ACLsAssignmentAttribute.inc b/plugins/management/acl/class_ACLsAssignmentAttribute.inc new file mode 100644 index 0000000000000000000000000000000000000000..09d7913012f0950fb34e67ee8a7603caa2ff48b7 --- /dev/null +++ b/plugins/management/acl/class_ACLsAssignmentAttribute.inc @@ -0,0 +1,111 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class ACLsAssignmentAttribute extends DialogOrderedArrayAttribute +{ + protected $order = TRUE; + protected $dialogClass = 'ACLsAssignmentDialog'; + protected $height = 300; + + protected function getAttributeArrayValue ($key, $value) + { + /* Convert text value to displayable array value */ + sort($value['members']); + static $nbShown = 4; + $members = join(', ', array_slice($value['members'], 0, $nbShown)); + if (count($value['members']) > $nbShown) { + $members .= sprintf(_(', and %d others'), (count($value['members']) - $nbShown)); + } + $value['members'] = $members; + return $value; + } + + function readValue ($value) + { + $acl = explode(':', $value); + return [$acl[0], [ + 'scope' => $acl[1], + 'role' => base64_decode($acl[2]), + 'members' => array_map('base64_decode', explode(',', $acl[3])), + 'userfilter' => (isset($acl[4]) ? base64_decode($acl[4]) : ''), + 'targetfilter' => (isset($acl[5]) ? base64_decode($acl[5]) : ''), + ]]; + } + + function writeValue ($key, $value) + { + return $key. + ':'.$value['scope']. + ':'.base64_encode($value['role']). + ':'.join(',', array_map('base64_encode', $value['members'])). + ':'.base64_encode($value['userfilter']). + ':'.base64_encode($value['targetfilter']); + } + + function foreignKeyUpdate ($oldvalue, $newvalue, array $source) + { + foreach ($this->value as $key => &$value) { + if ($source['FIELD'] == 'dn') { + if ($newvalue === NULL) { + if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $value['role'])) { + unset($this->value[$key]); + } + foreach ($value['members'] as $member_key => $member) { + if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $member)) { + unset($value['members'][$member_key]); + } + } + unset($member); + } elseif ($source['MODE'] == 'move') { + $value['role'] = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $value['role']); + foreach ($value['members'] as &$member) { + $member = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $member); + } + unset($member); + } elseif ($source['MODE'] == 'copy') { + /* Copied members are added */ + foreach ($value['members'] as $member) { + if (preg_match('/'.preg_quote($oldvalue, '/').'$/', $member)) { + $value['members'][] = preg_replace('/'.preg_quote($oldvalue, '/').'$/', $newvalue, $member); + } + } + } + } else { + trigger_error('unknown source "'.$source['CLASS'].'" with field "'.$source['FIELD'].'"'); + } + } + unset($value); + } + + function foreignKeyCheck ($oldvalue, array $source): bool + { + foreach ($this->value as $value) { + if ( + (($source['CLASS'] == 'aclRole') && ($value['role'] == $oldvalue)) || + (in_array($source['CLASS'], ['user','posixGroup','roleGeneric']) && in_array($oldvalue, $value['members'])) + ) { + return TRUE; + } elseif (!in_array($source['CLASS'], ['aclRole','user','posixGroup','roleGeneric'])) { + trigger_error('unknown source '.$source['CLASS']); + } + } + return FALSE; + } +} diff --git a/plugins/management/acl/class_ACLsAssignmentDialog.inc b/plugins/management/acl/class_ACLsAssignmentDialog.inc new file mode 100644 index 0000000000000000000000000000000000000000..ef6c11837dfd158e6d7fb64dd5dd56d37908b20f --- /dev/null +++ b/plugins/management/acl/class_ACLsAssignmentDialog.inc @@ -0,0 +1,68 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2013-2020 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class ACLsAssignmentDialog extends GenericDialog +{ + protected $initialAclValue; + protected $post_cancel = 'add_acl_cancel'; + protected $post_finish = 'add_acl_finish'; + protected $dialogClass = 'aclAssignmentDialogWindow'; + + function __construct ($simplePlugin, $attribute, $acl = NULL) + { + $isContainer = FALSE; + if (isset($simplePlugin->attrs['objectClass'])) { + if (count(array_intersect( + $simplePlugin->attrs['objectClass'], + ['organizationalUnit', 'organization', 'domain', 'country', 'locality']) + )) { + $isContainer = TRUE; + } + } else { + $isContainer = TRUE; + } + $this->attribute = $attribute; + $this->dialog = new $this->dialogClass($acl, $isContainer); + $this->dialog->dn = $simplePlugin->dn; + $this->initialAclValue = $acl; + } + + function handleFinish () + { + $this->dialog->readPost(); + $this->dialog->update(); + $messages = $this->dialog->check(); + if (!empty($messages)) { + msg_dialog::displayChecks($messages); + return; + } + $this->attribute->addValue('', $this->dialog->getAclEntry()); + unset($this->dialog); + } + + function handleCancel () + { + if ($this->initialAclValue !== NULL) { + $this->attribute->addValue('', $this->initialAclValue); + } + unset($this->dialog); + } +} diff --git a/plugins/management/acl/class_aclAssignment.inc b/plugins/management/acl/class_aclAssignment.inc new file mode 100644 index 0000000000000000000000000000000000000000..a8ec812198b827dbb57420872213a69214c3d399 --- /dev/null +++ b/plugins/management/acl/class_aclAssignment.inc @@ -0,0 +1,126 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class aclAssignment extends simplePlugin +{ + static function plInfo (): array + { + global $config; + $oc = [ + 'aclAssignment' => [ + 'aclCategory' => 'acl', + 'name' => _('ACL assignment'), + 'ou' => '', + 'icon' => 'geticon.php?context=categories&icon=acl&size=16', + 'tabClass' => 'simpleTabs_noSpecial', + 'mainAttr' => FALSE + ], + ]; + if ($config->get_cfg_value('aclTabOnObjects') == 'TRUE') { + $oc[] = 'special'; + } else { + $oc = array_merge($oc, departmentManagement::getDepartmentTypes()); + } + return [ + 'plShortName' => _('ACL Assignment'), + 'plDescription' => _('Access control roles assignment'), + 'plSmallIcon' => 'geticon.php?context=categories&icon=acl&size=16', + 'plObjectClass' => ['gosaAcl'], + 'plObjectType' => $oc, + 'plForeignKeys' => [ + 'gosaAclEntry' => [ + ['aclRole', 'dn', 'gosaAclEntry=*:*:%b|oldvalue%:*', 'gosaAclEntry=*'], + ['user', 'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*', 'gosaAclEntry=*'], + ['posixGroup', 'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*', 'gosaAclEntry=*'], + ['roleGeneric', 'dn', 'gosaAclEntry=*:*:*:*%b|oldvalue%*', 'gosaAclEntry=*'], + ] + ], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'main' => [ + 'name' => _('Assignments'), + 'class' => ['fullwidth'], + 'attrs' => [ + new ACLsAssignmentAttribute( + '', _('ACL role assignments for this base'), + 'gosaAclEntry', FALSE + ) + ] + ] + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, FALSE); + $this->attributesInfo['main']['name'] = sprintf(_('Assignments on object or subtree %s'), $this->dn); + } + + function compute_dn (): string + { + return $this->dn; + } + + function save (): array + { + $this->ignore_account = FALSE; + $this->is_account = (count($this->gosaAclEntry) != 0); + if ($this->is_account) { + return parent::save(); + } else { + return $this->remove(FALSE); + } + } + + function post_remove () + { + parent::post_remove(); + + /* Refresh users ACLs */ + $ui = get_userinfo(); + $ui->loadACL(); + session::set('ui', $ui); + } + + function post_save () + { + parent::post_save(); + + /* Refresh users ACLs */ + $ui = get_userinfo(); + $ui->loadACL(); + session::set('ui', $ui); + } + + function foreignKeyUpdate (string $field, $oldvalue, $newvalue, array $source) + { + /* Handle foreign keys only as the main tab so that they are not handled twice */ + if ($this->parent->getBaseObject() !== $this) { + return; + } + parent::foreignKeyUpdate($field, $oldvalue, $newvalue, $source); + } +} diff --git a/plugins/management/acl/class_aclAssignmentDialogWindow.inc b/plugins/management/acl/class_aclAssignmentDialogWindow.inc new file mode 100644 index 0000000000000000000000000000000000000000..40836188e8e6d7a871eab7a7a64d6a08dd2c2379 --- /dev/null +++ b/plugins/management/acl/class_aclAssignmentDialogWindow.inc @@ -0,0 +1,144 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class aclAssignmentDialogWindow extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('ACL Assignment Dialog'), + 'plDescription' => _('Access control roles assignment dialog'), + 'plCategory' => ['acl'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'properties' => [ + 'name' => _('Properties'), + 'attrs' => [ + new SelectAttribute( + _('Mode'), _('Is this applying on complete subtree or only the base?'), + 'aclMode', TRUE, + ['subtree', 'base'], 'base', + [_('Subtree'), _('Base only')] + ), + new SelectAttribute( + _('Role'), _('Role to apply'), + 'aclRole', TRUE, + [] + ), + new BooleanAttribute( + _('For all users'), _('Apply this ACL for all LDAP users'), + 'allUsers', FALSE + ), + new UsersGroupsRolesAttribute( + _('Members'), _('Users or groups to assign this role to.'), + 'aclMembers', TRUE + ), + ] + ], + 'advanced' => [ + 'name' => _('Advanced'), + 'attrs' => [ + new DisplayAttribute( + '', _('Warning message'), + 'advancedWarning', FALSE, + _('These options allow you to restrict members or targets by LDAP filter. The member filter cannot use masks, and is safe to return any number of results as it will only be applied to the logged in user. For target filter, please avoid using a filter returning too many results, and if using template masks, avoid doing so on user-editable fields.') + ), + new StringAttribute( + _('Member filter'), _('LDAP filter which a member must match to actually get the rights'), + 'aclUserFilter', FALSE + ), + new StringAttribute( + _('Target filter'), _('LDAP filter which a dn must match to actually be concerned. May use template masks for user fields. Example: (manager=%dn%).'), + 'aclTargetFilter', FALSE + ), + ] + ], + ]; + } + + function __construct ($value, $isContainer = FALSE) + { + parent::__construct(NULL, NULL, NULL, TRUE); + if ($isContainer) { + $this->attributesAccess['aclMode']->setDefaultValue('subtree'); + } else { + $this->attributesAccess['aclMode']->setDefaultValue('base'); + } + $this->attributesAccess['aclMode']->resetToDefault(); + + $roles = objects::ls('aclRole', 'cn'); + $this->attributesAccess['aclRole']->setChoices(array_keys($roles), array_values($roles)); + $this->attributesAccess['allUsers']->setInLdap(FALSE); + $this->attributesAccess['allUsers']->setManagedAttributes( + [ + 'disable' => [ + TRUE => ['aclMembers'] + ] + ] + ); + + if ($value !== NULL) { + $this->aclMode = $value['scope']; + $this->aclRole = $value['role']; + $this->aclMembers = $value['members']; + if ($value['members'][0] == '*') { + $this->allUsers = TRUE; + } + $this->aclUserFilter = $value['userfilter']; + $this->aclTargetFilter = $value['targetfilter']; + } + } + + public function render (): string + { + $smarty = get_smarty(); + $display = parent::render(); + if (!is_object($this->dialog)) { + $display .= $smarty->fetch('string:'. + '<p class="plugbottom">'. + ' <input type="submit" name="add_acl_finish" value="{msgPool type=addButton}"/>'. + ' '. + ' <input type="submit" formnovalidate="formnovalidate" name="add_acl_cancel" value="{msgPool type=cancelButton}"/>'. + '</p>'); + } + return $display; + } + + function getAclEntry () + { + $entry = [ + 'scope' => $this->aclMode, + 'role' => $this->aclRole, + 'members' => $this->aclMembers, + 'userfilter' => $this->aclUserFilter, + 'targetfilter' => $this->aclTargetFilter, + ]; + if ($this->allUsers) { + $entry['members'] = ['*']; + } + return $entry; + } +} diff --git a/plugins/management/acl/class_aclManagement.inc b/plugins/management/acl/class_aclManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..9d8c0c4039a50312eeb6d0a74085a9e6ba119b0b --- /dev/null +++ b/plugins/management/acl/class_aclManagement.inc @@ -0,0 +1,214 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2020 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class aclAssignmentCreationDialog extends simplePlugin +{ + protected $post_finish = 'add_finish'; + protected $post_cancel = 'add_cancel'; + protected $management; + protected $closed = FALSE; + + static function plInfo (): array + { + return [ + 'plShortName' => _('ACL assignment creation'), + 'plTitle' => _('ACL assignment creation'), + 'plDescription' => _('Create an ACL assignment on an arbitrary dn'), + 'plCategory' => ['acl'], + + 'plProvidedAcls' => [] + ]; + } + + static function getAttributesInfo (): array + { + global $config; + return [ + 'main' => [ + 'name' => _('ACL assignment creation'), + 'class' => ['fullwidth'], + 'attrs' => [ + new StringAttribute( + _('Dn'), _('DN you wish to add assignments for'), + 'baseDn', TRUE, + $config->current['BASE'], 'aclMode', + '/^([^=,]+=[^=,]+,)+'.preg_quote($config->current['BASE'], '/').'$/', + 'ou=people,'.$config->current['BASE'] + ), + ] + ], + ]; + } + + function __construct ($parent) + { + global $config; + + $this->management = $parent; + + parent::__construct($config->current['BASE']); + + $this->attributesAccess['baseDn']->setInLdap(FALSE); + } + + public function readPost () + { + parent::readPost(); + if (isset($_POST[$this->post_cancel])) { + $this->handleCancel(); + } elseif (isset($_POST[$this->post_finish])) { + $this->handleFinish(); + } + } + + public function update (): bool + { + parent::update(); + return !$this->closed; + } + + public function render (): string + { + $smarty = get_smarty(); + $smarty->assign($this->attributesAccess['baseDn']->getAcl().'ACL', 'rwcdm'); + return parent::render()."\n". + '<p class="plugbottom">'."\n". + ' <input type="submit" name="'.$this->post_finish.'" value="'.msgPool::addButton().'"/> '."\n". + ' <input type="submit" formnovalidate="formnovalidate" name="'.$this->post_cancel.'" value="'.msgPool::cancelButton().'"/>'."\n". + '</p>'; + } + + public function handleFinish () + { + $msgs = $this->check(); + if (count($msgs)) { + msg_dialog::displayChecks($msgs); + return; + } + try { + $this->management->newEntryConfirmed($this->baseDn); + } catch (NonExistingLdapNodeException $e) { + $error = new FusionDirectoryError(htmlescape(_('The dn you entered could not be found in LDAP')), 0, $e); + $error->display(); + return; + } + $this->closed = TRUE; + } + + function handleCancel () + { + $this->management->closeDialogs(); + } + + function save (): array + { + return []; + } +} + +class aclManagement extends management +{ + protected $skipCpHandler = TRUE; + + public static $skipSnapshots = TRUE; + + public static $skipTemplates = TRUE; + + /* We cannot show anything more than DN as there are no ACLs for other fields on aclAssignment object */ + public static $columns = [ + ['ObjectTypeColumn', []], + ['LinkColumn', ['attributes' => 'dn', 'label' => 'DN']], + ['ActionsColumn', ['label' => 'Actions']], + ]; + + static function plInfo (): array + { + return [ + 'plShortName' => _('ACL assignments'), + 'plTitle' => _('ACL assignments management'), + 'plDescription' => _('Manage ACL roles assignments to users'), + 'plIcon' => 'geticon.php?context=categories&icon=acl&size=48', + 'plSection' => 'accounts', + 'plPriority' => 28, + 'plManages' => ['aclAssignment'], + + 'plProvidedAcls' => [] + ]; + } + + protected function setUpListing () + { + /* Set baseMode to FALSE */ + $this->listing = new managementListing($this, FALSE); + } + + protected function setUpFilter (array $filterElementDefinitions) + { + parent::setUpFilter($filterElementDefinitions); + /* Set scope to sub */ + $this->filter->setScope('sub'); + } + + /*! + * \brief Opens aclAssignmentCreationDialog to enter an arbitrary DN + */ + function newEntry (array $action) + { + $this->dialogObject = new aclAssignmentCreationDialog($this); + } + + + /*! + * \brief Called by aclAssignmentCreationDialog + */ + function newEntryConfirmed ($dn) + { + $type = 'aclAssignment'; + + /* This might throw an exception which is catched in aclAssignmentCreationDialog */ + $tabObject = objects::open($dn, $type); + + /* This will happen only if the exception is not thrown */ + $this->closeDialogs(); + + $this->currentDn = $dn; + + $this->openTabObject($tabObject); + logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $this->currentDn, 'Creating new ACLÂ assignment'); + } + + /*! \brief Display confirmation dialog + */ + protected function removeConfirmationDialog (array $objects) + { + $smarty = get_smarty(); + $smarty->assign('objects', $objects); + $smarty->assign('multiple', TRUE); + return $smarty->fetch(get_template_path('remove.tpl', TRUE, dirname(__FILE__))); + } + + protected function renderFilter (): string + { + /* Hide filter box */ + return ''; + } +} diff --git a/plugins/management/acl/remove.tpl b/plugins/management/acl/remove.tpl new file mode 100644 index 0000000000000000000000000000000000000000..817e0e926477afd3f690c6fbc376af3084225f2e --- /dev/null +++ b/plugins/management/acl/remove.tpl @@ -0,0 +1,28 @@ +<div class="notice"> + <div class="msgtitle"> + <h2> + <img alt="" src="geticon.php?context=status&icon=dialog-warning&size=32" class="center"/> {t}Warning: you are about to delete the ACL assignments on the following objects{/t} + </h2> + </div> + <div> + <p> + <ul> + {foreach from=$objects item=object} + <li style="list-style-image:url('{$object.icon}');" title="{$object.type}"> + {$object.name} (<i>{$object.dn}</i>) + </li> + {/foreach} + </ul> + {t}This includes all ACL assignments made on this node(s). If you want the list of these assignments, please cancel and open the objects.{/t} + </p> + + <p> + {t}Press 'Delete' to continue or 'Cancel' to abort.{/t} + </p> + + <p class="plugbottom"> + <input type="submit" name="delete_confirmed" value="{msgPool type=delButton}"/> + <input type="submit" formnovalidate="formnovalidate" name="delete_cancel" value="{msgPool type=cancelButton}"/> + </p> + </div> +</div> diff --git a/plugins/management/aclrole/acleditiondialog.tpl b/plugins/management/aclrole/acleditiondialog.tpl new file mode 100644 index 0000000000000000000000000000000000000000..a5691848d244ef3f9e3cf547d4e09e667771620a --- /dev/null +++ b/plugins/management/aclrole/acleditiondialog.tpl @@ -0,0 +1,140 @@ + <h1>{$headline|escape}</h1> + +{if $dialogState eq 'create'} + {$aclEdition} + <p class="plugbottom"> + <input type="submit" name="add_finish" value="{msgPool type=applyButton}"/> + <input type="submit" formnovalidate="formnovalidate" name="add_cancel" value="{msgPool type=cancelButton}"/> + </p> +{elseif $dialogState eq 'edit'} + {function makeCheckbox key='' infos='' attr='' acl='' rights=''} + {$checked = (strpos($rights, $acl) !== FALSE)} + <input id="acl_{$infos.tname}_{$attr}_{$acl}" type="checkbox" name="acl_{$key}_{$attr}_{$acl}"{if $checked} checked="checked"{/if}/> + <label for="acl_{$infos.tname}_{$attr}_{$acl}">{$label}</label> + {/function} + + <input type="hidden" name="acl_dummy_0_0_0" value="1"/> + + <div class="acledition"> + + <div class="togglebuttons"> + <input type="button" name="toggle_all_create" onClick="acl_toggle_all('_0_c$');" value="Toggle C"/> + <input type="button" name="toggle_all_move" onClick="acl_toggle_all('_0_m$');" value="Toggle M"/> + <input type="button" name="toggle_all_remove" onClick="acl_toggle_all('_0_d$');" value="Toggle D"/> + - + <input type="button" name="toggle_all_read" onClick="acl_toggle_all('_0_r$');" value="Toggle R"/> + <input type="button" name="toggle_all_write" onClick="acl_toggle_all('_0_w$');" value="Toggle W"/> + - + <input type="button" name="toggle_all_sub_read" onClick="acl_toggle_all('[^0]_r$');" value="R+"/> + <input type="button" name="toggle_all_sub_write" onClick="acl_toggle_all('[^0]_w$');" value="W+"/> + </div> + <div class="setbuttons"> + <input type="button" name="set_true_all_create" onClick="acl_set_all('_0_c$',true);" value="C+"/><input type="button" name="set_false_all_create" onClick="acl_set_all('_0_c$',false);" value="C-"/> + <input type="button" name="set_true_all_move" onClick="acl_set_all('_0_m$',true);" value="M+"/><input type="button" name="set_false_all_move" onClick="acl_set_all('_0_m$',false);" value="M-"/> + <input type="button" name="set_true_all_remove" onClick="acl_set_all('_0_d$',true);" value="D+"/><input type="button" name="set_false_all_remove" onClick="acl_set_all('_0_d$',false);" value="D-"/> + - + <input type="button" name="set_true_all_read" onClick="acl_set_all('_0_r$',true);" value="R+"/><input type="button" name="set_false_all_read" onClick="acl_set_all('_0_r$',false);" value="R-"/> + <input type="button" name="set_true_all_write" onClick="acl_set_all('_0_w$',true);" value="W+"/><input type="button" name="set_false_all_write" onClick="acl_set_all('_0_w$',false);" value="W-"/> + - + <input type="button" name="set_true_all_read" onClick="acl_set_all('[^0]_r$',true);" value="R+"/><input type="button" name="set_false_all_read" onClick="acl_set_all('[^0]_r$',false);" value="R-"/> + <input type="button" name="set_true_all_write" onClick="acl_set_all('[^0]_w$',true);" value="W+"/><input type="button" name="set_false_all_write" onClick="acl_set_all('[^0]_w$',false);" value="W-"/> + </div> + + {foreach from=$aclObjects key=$key item=$infos} + <table {if $infos.expand}class="expand"{/if}> + <tbody> + <tr> + <td colspan="2"> + <b>{t}Object{/t}: {$infos.name}</b> + </td> + <td align="right"> + {if !empty($infos.attrs)} + <input id="show{$infos.tname}" type="button" onclick="$('{$infos.tname}').toggle();" value="{t}Show/hide advanced settings{/t}"/> + {/if} + </td> + </tr> + <tr> + <td colspan="2"> + {if $infos.rights.create} + {makeCheckbox key=$key infos=$infos attr=0 acl=c label=_("Create objects") rights=$infos.globalAcl} + {/if} + {if $infos.rights.move} + {makeCheckbox key=$key infos=$infos attr=0 acl=m label=_("Move objects") rights=$infos.globalAcl} + {/if} + {if $infos.rights.remove} + {makeCheckbox key=$key infos=$infos attr=0 acl=d label=_("Remove objects") rights=$infos.globalAcl} + {/if} + {if $infos.rights.self} + {makeCheckbox key=$key infos=$infos attr=0 acl=s label=_("Grant permission to owner") rights=$infos.globalAcl} + {/if} + </td> + <td> + {if ($infos.rights.read||$infos.rights.write)} + {t}Complete object{/t}: + {if $infos.rights.read} + {makeCheckbox key=$key infos=$infos attr=0 acl=r label=_("read") rights=$infos.globalAcl} + {/if} + {if $infos.rights.write} + {makeCheckbox key=$key infos=$infos attr=0 acl=w label=_("write") rights=$infos.globalAcl} + {/if} + {/if} + </td> + </tr> + <tr id="tr_{$infos.tname}"> + <td colspan="3"> + <div id="{$infos.tname}" style="display:none;"> + <table> + <tbody> + {* Walk through the list of attributes *} + {$cnt = 1} + {foreach from=$infos.attrs key=$attr item=$dsc} + {* Skip pl* attributes, they are internal... *} + {if preg_match('/^pl[A-Z]+.*$/', $attr)} + continue; + {/if} + + {* Open table row *} + {if ($cnt == 1)} + <tr> + {/if} + + {* Collect list of attributes *} + {$state = ''} + {if isset($infos.acl[$attr])} + {$state = $infos.acl[$attr]} + {/if} + <td> + <b>{$dsc}</b> ({$attr})<br/> + {makeCheckbox key=$key infos=$infos attr=$attr acl=r label=_("read") rights=$state} + {makeCheckbox key=$key infos=$infos attr=$attr acl=w label=_("write") rights=$state} + </td> + + {* Close table row *} + {if ($cnt == 3)} + {$cnt = 1} + </tr> + {else} + {$cnt = $cnt+1} + {/if} + {/foreach} + {* Fill missing td's if needed *} + {while (($cnt == 3) || ($cnt == 2))} + <td> </td> + {$cnt = $cnt+1} + {/while} + </tbody> + </table> + </div> + </td> + </tr> + </tbody> + </table> + {/foreach} + + </div> + + <p class="plugbottom"> + <input type="submit" name="submit_edit_acl" value="{msgPool type=applyButton}"/> + <input type="submit" formnovalidate="formnovalidate" name="cancel_edit_acl" value="{msgPool type=cancelButton}"/> + </> +{/if} diff --git a/plugins/management/aclrole/class_aclEditionDialog.inc b/plugins/management/aclrole/class_aclEditionDialog.inc new file mode 100644 index 0000000000000000000000000000000000000000..9dc188993ec930009e0c7561c39ac59c249dbdc3 --- /dev/null +++ b/plugins/management/aclrole/class_aclEditionDialog.inc @@ -0,0 +1,314 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2020 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* ACL categories list */ +class ACLEditionDialog implements FusionDirectoryDialog +{ + protected $attribute; + + protected $post_cancel = 'add_cancel'; + protected $post_finish = 'add_finish'; + + protected $initialAclValue; + protected $dialogState = 'create'; + protected $aclObject = ''; + protected $aclContents = []; + protected $savedAclContents = []; + + function __construct ($simplePlugin, $attribute, $acl = NULL) + { + $this->attribute = $attribute; + $this->initialAclValue = $acl; + + /* New entry gets presets... */ + if ($acl === NULL) { + $this->aclContents = []; + } else { + $this->aclContents = $acl; + } + } + + public function readPost () + { + global $config; + + if (isset($_POST[$this->post_cancel])) { + $this->handleCancel(); + return; + } elseif (isset($_POST[$this->post_finish]) || isset($_GET[$this->post_finish])) { + $this->handleFinish(); + return; + } + + $new_acl = []; + + foreach ($_POST as $name => $post) { + /* Actions... */ + if (preg_match('/^cat_edit_.*_x/', $name)) { + $this->aclObject = preg_replace('/^cat_edit_([^_]+)_.*$/', '\1', $name); + $this->dialogState = 'edit'; + foreach ($config->data['CATEGORIES'][$this->aclObject]['classes'] as $oc) { + if (isset($this->aclContents[$oc])) { + $this->savedAclContents[$oc] = $this->aclContents[$oc]; + } + if (isset($this->aclContents[$this->aclObject.'/'.$oc])) { + $this->savedAclContents[$this->aclObject.'/'.$oc] = $this->aclContents[$this->aclObject.'/'.$oc]; + } + } + break; + } + + if (preg_match('/^cat_del_.*_x/', $name)) { + $idx = preg_replace('/^cat_del_([^_]+)_.*$/', '\1', $name); + foreach ($config->data['CATEGORIES'][$idx]['classes'] as $key) { + unset($this->aclContents["$idx/$key"]); + } + break; + } + + /* ACL checkbox saving... */ + if (preg_match('/^acl_([^_]+)_(.*)_([^_yx])$/', $name, $matches)) { + $object = $matches[1]; + $attribute = $matches[2]; + $value = $matches[3]; + + /* Skip for detection entry */ + if ($object == 'dummy') { + continue; + } + + /* Ordinary ACL */ + if (!isset($new_acl[$object])) { + $new_acl[$object] = []; + } + if (isset($new_acl[$object][$attribute])) { + $new_acl[$object][$attribute] .= $value; + } else { + $new_acl[$object][$attribute] = $value; + } + } + } + + /* Only be interested in new acl's, if we're in the right _POST place */ + if (isset($_POST['acl_dummy_0_0_0']) && ($this->aclObject != '') && is_array($config->data['CATEGORIES'][$this->aclObject])) { + foreach ($config->data['CATEGORIES'][$this->aclObject]['classes'] as $oc) { + unset($this->aclContents[$oc]); + unset($this->aclContents[$this->aclObject.'/'.$oc]); + if (isset($new_acl[$oc])) { + $this->aclContents[$oc] = $new_acl[$oc]; + } + if (isset($new_acl[$this->aclObject.'/'.$oc])) { + $this->aclContents[$this->aclObject.'/'.$oc] = $new_acl[$this->aclObject.'/'.$oc]; + } + } + } + + if ($this->dialogState == 'edit') { + /* Cancel edit acl? */ + if (isset($_POST['cancel_edit_acl'])) { + $this->dialogState = 'create'; + foreach ($config->data['CATEGORIES'][$this->aclObject]['classes'] as $oc) { + if (isset($this->savedAclContents[$oc])) { + $this->aclContents[$oc] = $this->savedAclContents[$oc]; + } + if (isset($this->savedAclContents[$this->aclObject.'/'.$oc])) { + $this->aclContents[$this->aclObject.'/'.$oc] = $this->savedAclContents[$this->aclObject.'/'.$oc]; + } + } + } + + /* Save edit acl? */ + if (isset($_POST['submit_edit_acl'])) { + $this->dialogState = 'create'; + } + } + } + + public function update (): bool + { + return ($this->dialogState !== 'closed'); + } + + protected function handleFinish () + { + $this->attribute->addValue('', $this->aclContents); + $this->dialogState = 'closed'; + } + + protected function handleCancel () + { + if ($this->initialAclValue !== NULL) { + $this->attribute->addValue('', $this->initialAclValue); + } + $this->dialogState = 'closed'; + } + + public function render (): string + { + global $config; + + /* Create templating instance */ + $smarty = get_smarty(); + $smarty->assign('usePrototype', 'true'); + + if ($this->dialogState == 'create') { + /* Draw list */ + $aclList = new divSelectBox('aclList'); + $aclList->setHeight('auto'); + + /* Add settings for all categories to the (permanent) list */ + foreach ($config->data['CATEGORIES'] as $section => $infos) { + if (($section != 'all') && (count($infos['classes']) == 1)) { + /* Hide empty categories */ + continue; + } + $summary = []; + foreach ($infos['classes'] as $oc) { + if (isset($this->aclContents[$oc]) && count($this->aclContents[$oc]) && isset($this->aclContents[$oc][0]) && + $this->aclContents[$oc][0] != '') { + $summary[] = $oc; + continue; + } + if (isset($this->aclContents["$section/$oc"]) && count($this->aclContents["$section/$oc"])) { + $summary[] = $oc; + continue; + } + if (isset($this->aclContents[$oc]) && !isset($this->aclContents[$oc][0]) && count($this->aclContents[$oc])) { + $summary[] = $oc; + } + } + + /* Set summary... */ + if (empty($summary)) { + $summary = '<i>'._('No ACL settings for this category').'</i>'; + } else { + $summary = sprintf(_('ACL for these objects: %s'), join(', ', $summary)); + } + + $action = '<input class="center" type="image" src="geticon.php?context=actions&icon=document-edit&size=16" + alt="'._('Edit').'" name="cat_edit_'.$section.'" title="'._('Edit category ACL').'"/>'. + '<input class="center" type="image" src="geticon.php?context=actions&icon=edit-delete&size=16" + alt="'._('Delete').'" name="cat_del_'.$section.'" title="'._('Reset category ACL').'"/>'; + + $field1 = ['html' => $infos['description'], 'attach' => 'style="width:140px"']; + $field2 = ['html' => $summary]; + $field3 = ['html' => $action, 'attach' => 'style="border-right:0px;width:40px"']; + $aclList->addEntry([$field1, $field2, $field3]); + } + + $smarty->assign('headline', _('List of available ACL categories')); + $smarty->assign('aclEdition', $aclList->drawList()); + } elseif ($this->dialogState == 'edit') { + /* Collect objects for selected category */ + $aclObjects = []; + foreach ($config->data['CATEGORIES'][$this->aclObject]['classes'] as $idx => $class) { + if ($idx == 0) { + continue; + } + $pInfos = pluglist::pluginInfos($class); + $key = $this->aclObject.'/'.$class; + /* Create sub acl if it does not exist */ + if (!isset($this->aclContents[$key])) { + $this->aclContents[$key] = []; + } + if (!isset($this->aclContents[$key][0])) { + $this->aclContents[$key][0] = ''; + } + if ((strtolower($class) === 'archivedobject') && (strtolower($this->aclObject) !== 'archivedobject')) { + $attrs = []; + $rights = [ + 'create' => TRUE, + 'move' => FALSE, + 'remove' => FALSE, + 'read' => FALSE, + 'write' => FALSE, + 'self' => FALSE, + ]; + } else { + $attrs = $pInfos['plProvidedAcls']; + $rights = [ + 'create' => TRUE, + 'move' => TRUE, + 'remove' => TRUE, + 'read' => TRUE, + 'write' => TRUE, + 'self' => $pInfos['plSelfModify'], + ]; + } + $aclObjects[$key] = [ + 'name' => $pInfos['plShortName'], + 'tname' => preg_replace('/[^a-z0-9]/i', '_', $key), + 'acl' => $this->aclContents[$key], + 'globalAcl' => $this->aclContents[$key][0], + 'expand' => ((count($this->aclContents[$key]) > 1) || !empty($this->aclContents[$key][0])), + 'attrs' => $attrs, + 'rights' => $rights, + ]; + } + if ($this->aclObject == 'all') { + /* Create sub acl if it does not exist */ + $key = 'all'; + if (!isset($this->aclContents[$key])) { + $this->aclContents[$key] = []; + } + if (!isset($this->aclContents[$key][0])) { + $this->aclContents[$key][0] = ''; + } + $aclObjects['all'] = [ + 'name' => _('All objects in current subtree'), + 'tname' => preg_replace('/[^a-z0-9]/i', '_', $key), + 'acl' => $this->aclContents[$key], + 'globalAcl' => $this->aclContents[$key][0], + 'expand' => ((count($this->aclContents[$key]) > 1) || !empty($this->aclContents[$key][0])), + 'attrs' => [], + 'rights' => [ + 'create' => TRUE, + 'move' => TRUE, + 'remove' => TRUE, + 'read' => TRUE, + 'write' => TRUE, + 'self' => FALSE, + ], + ]; + } + if (isset($aclObjects[$this->aclObject.'/template'])) { + /* Move template to the end */ + $data = $aclObjects[$this->aclObject.'/template']; + unset($aclObjects[$this->aclObject.'/template']); + $aclObjects[$this->aclObject.'/template'] = $data; + } + if (isset($aclObjects[$this->aclObject.'/archivedObject'])) { + /* Move archivedObject to the end */ + $data = $aclObjects[$this->aclObject.'/archivedObject']; + unset($aclObjects[$this->aclObject.'/archivedObject']); + $aclObjects[$this->aclObject.'/archivedObject'] = $data; + } + $smarty->assign('headline', sprintf(_('Edit ACL for "%s"'), $config->data['CATEGORIES'][$this->aclObject]['description'])); + $smarty->assign('aclObjects', $aclObjects); + } + + /* Show main page */ + $smarty->assign('dialogState', $this->dialogState); + + return $smarty->fetch(get_template_path('acleditiondialog.tpl', dirname(__FILE__))); + } +} diff --git a/plugins/management/aclrole/class_aclRole.inc b/plugins/management/aclrole/class_aclRole.inc new file mode 100644 index 0000000000000000000000000000000000000000..64d9ecb51cd6ab7e57c20bfc0449a4ff6ae0fd1c --- /dev/null +++ b/plugins/management/aclrole/class_aclRole.inc @@ -0,0 +1,119 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* complicated stuff */ +class ACLsAttribute extends DialogOrderedArrayAttribute +{ + protected $order = TRUE; + protected $dialogClass = 'ACLEditionDialog'; + + protected function getAttributeArrayValue ($key, $value) + { + /* Convert text value to displayable array value */ + + /* Summarize ACL */ + $summary = ''; + foreach ($value as $name => $object) { + if (count($object)) { + $summary .= $name.', '; + } + } + $summary = sprintf(_('Contains settings for these objects: %s'), preg_replace('/, $/', '', $summary)); + return [$summary]; + } + + function readValue ($value) + { + $acl = explode(':', $value, 2); + return [$acl[0], acl::extractACL($acl[1])]; + } + + function writeValue ($key, $value) + { + $acl = ''; + foreach ($value as $object => $contents) { + /* Only save, if we've some contents in there... */ + if (count($contents)) { + $acl .= $object.';'; + foreach ($contents as $attr => $permission) { + /* First entry? Its the one for global settings... */ + if ($attr == '0') { + $acl .= $permission; + } else { + $acl .= '#'.$attr.';'.$permission; + } + } + $acl .= ','; + } + } + return $key.':'.preg_replace('/,$/', '', $acl); + } +} + +class aclRole extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Role'), + 'plDescription' => _('Access control roles'), + 'plObjectClass' => ['gosaRole'], + 'plObjectType' => ['aclRole' => [ + 'aclCategory' => 'acl', + 'name' => _('ACL role'), + 'ou' => get_ou('aclRoleRDN'), + 'icon' => 'geticon.php?context=categories&icon=acl&size=16', + ]], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'properties' => [ + 'name' => _('Properties'), + 'attrs' => [ + new BaseSelectorAttribute(get_ou('aclRoleRDN')), + new HostNameAttribute( + _('Name'), _('A name for this role'), + 'cn', TRUE + ), + new TextAreaAttribute( + _('Description'), _('Short description of this role'), + 'description', FALSE + ), + ] + ], + 'acls' => [ + 'name' => _('ACLs'), + 'class' => ['fullwidth'], + 'attrs' => [ + new ACLsAttribute( + '', _('ACLs which are part of this group'), + 'gosaAclTemplate', TRUE + ), + ] + ], + ]; + } +} diff --git a/plugins/management/aclrole/class_aclRoleManagement.inc b/plugins/management/aclrole/class_aclRoleManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..b17c194bee2c50f882b2dc09f047b6d379e5a69f --- /dev/null +++ b/plugins/management/aclrole/class_aclRoleManagement.inc @@ -0,0 +1,38 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2018 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class aclRoleManagement extends management +{ + public static function plInfo (): array + { + return [ + 'plShortName' => _('ACL roles'), + 'plTitle' => _('ACL roles management'), + 'plDescription' => _('Manage ACL roles'), + 'plIcon' => 'geticon.php?context=categories&icon=acl&size=48', + 'plSection' => 'accounts', + 'plPriority' => 27, + 'plCategory' => ['acl'], + 'plManages' => ['aclRole'], + + 'plProvidedAcls' => [] + ]; + } +} diff --git a/plugins/management/departments/class_country.inc b/plugins/management/departments/class_country.inc new file mode 100644 index 0000000000000000000000000000000000000000..a1baf7bac31945036be6229d812cc5084937b840 --- /dev/null +++ b/plugins/management/departments/class_country.inc @@ -0,0 +1,65 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class country extends department +{ + static protected $namingAttr = 'c'; + protected $structuralOC = ['country']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Country'), + 'plDescription' => _('Country'), + 'plObjectClass' => ['gosaDepartment'], + 'plFilter' => '(&(objectClass=country)(objectClass=gosaDepartment))', + 'plObjectType' => ['country' => [ + 'aclCategory' => 'department', + 'name' => _('Country'), + 'ou' => '', + 'icon' => 'geticon.php?context=types&icon=country&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ]], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + $attributesInfo = parent::getDepartmentAttributesInfo(_('country')); + unset($attributesInfo['location']); + unset($attributesInfo['properties']['attrs'][3]); // category + /* Replace Name field by country code */ + $attributesInfo['properties']['attrs'][1] = + new StringAttribute( + _('Country code'), _('A two-letter country code from ISO 3166'), + static::$namingAttr, TRUE, + '', '', + '/^[a-z0-9\.-]{2}$/i', 'IT' + ); + return $attributesInfo; + } +} diff --git a/plugins/management/departments/class_dcObject.inc b/plugins/management/departments/class_dcObject.inc new file mode 100644 index 0000000000000000000000000000000000000000..ee8284755d61b0d76567a9d6e58d5135d95c4fb0 --- /dev/null +++ b/plugins/management/departments/class_dcObject.inc @@ -0,0 +1,56 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2106 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class dcObject extends department +{ + static protected $namingAttr = 'dc'; + protected $structuralOC = ['locality']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Domain Component'), + 'plDescription' => _('Domain Component'), + 'plObjectClass' => ['dcObject', 'gosaDepartment'], + 'plObjectType' => ['dcObject' => [ + 'aclCategory' => 'department', + 'name' => _('Domain Component'), + 'ou' => '', + 'icon' => 'geticon.php?context=types&icon=dc&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ]], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + $attributesInfo = parent::getDepartmentAttributesInfo(_('domain component')); + unset($attributesInfo['location']); + unset($attributesInfo['properties']['attrs'][3]); // category + return $attributesInfo; + } +} diff --git a/plugins/management/departments/class_department.inc b/plugins/management/departments/class_department.inc new file mode 100644 index 0000000000000000000000000000000000000000..777b69529f650bb196ee3aea426e28e302111777 --- /dev/null +++ b/plugins/management/departments/class_department.inc @@ -0,0 +1,200 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class department extends simplePlugin +{ + static protected $namingAttr = 'ou'; + protected $structuralOC = ['organizationalUnit']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Department'), + 'plDescription' => _('Departments'), + /* Do not append the structural object classes here, they are added dynamically in the constructor */ + 'plObjectClass' => ['gosaDepartment'], + 'plFilter' => '(&(objectClass=organizationalUnit)(objectClass=gosaDepartment))', + 'plCategory' => ['department' => ['objectClass' => 'gosaDepartment', 'description' => _('Departments')]], + 'plObjectType' => [ + 'department' => [ + 'name' => _('Department'), + 'ou' => '', + 'icon' => 'geticon.php?context=places&icon=folder&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ] + ], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return static::getDepartmentAttributesInfo(_('department')); + } + static function getDepartmentAttributesInfo ($name) + { + $attributesInfo = [ + 'properties' => [ + 'name' => _('Properties'), + 'attrs' => [ + new BaseSelectorAttribute(''), + new StringAttribute( + sprintf(_('Name of %s'), $name), sprintf(_('A name for this %s'), $name), + static::$namingAttr, TRUE, + '', '', '/^[a-z0-9\._-]+$/i' + ), + new TextAreaAttribute( + _('Description'), sprintf(_('Short description of this %s'), $name), + 'description', TRUE + ), + new SelectAttribute( + _('Category'), sprintf(_('Category of this %s'), $name), + 'businessCategory', FALSE + ), + new StringAttribute( + _('Website'), sprintf(_('Website of this %s'), $name), + 'labeledURI', FALSE + ), + new UserAttribute( + _('Manager'), sprintf(_('Manager of this %s'), $name), + 'manager', FALSE + ) + ] + ], + 'location' => [ + 'name' => _('Location'), + 'attrs' => [ + new StringAttribute( + _('State'), _('State'), 'st', FALSE + ), + new StringAttribute( + _('Location'), _('Location'), 'l', FALSE + ), + new StringAttribute( + _('Country'), _('Country'), 'co', FALSE + ), + new PostalAddressAttribute( + _('Address'), sprintf(_('A postal address for this %s'), $name), + 'postalAddress', FALSE + ), + new PhoneNumberButtonAttribute( + _('Phone'), _('Telephone number'), + 'telephoneNumber', FALSE, + '', + 'any' + ), + new PhoneNumberAttribute( + _('Fax'), _('Facsimile telephone number'), + 'facsimileTelephoneNumber', FALSE + ), + ] + ], + ]; + if (static::$namingAttr != 'ou') { + $attributesInfo['properties']['attrs'][] = new HiddenAttribute('ou'); + } + return $attributesInfo; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + + parent::__construct($dn, $object, $parent, $mainTab); + + /* Add the default structural object class if this is a new entry */ + $ldap = $config->get_ldap_link(); + $ldap->cd($config->current['BASE']); + if (($dn == '') || ($dn == 'new') || !$ldap->dn_exists($dn)) { + $this->objectclasses = array_merge($this->structuralOC, $this->objectclasses); + } else { + $ldap->cat($dn, ['structuralObjectClass']); + $attrs = $ldap->fetch(); + if (isset($attrs['structuralObjectClass']['count'])) { + for ($i = 0; $i < $attrs['structuralObjectClass']['count']; $i++) { + $this->objectclasses[] = $attrs['structuralObjectClass'][$i]; + } + } else { + /* Could not detect structural object class for this object, fall back to the default */ + $this->objectclasses = array_merge($this->structuralOC, $this->objectclasses); + } + } + $this->objectclasses = array_unique($this->objectclasses); + + $categoriesList = $config->get_cfg_value('DepartmentCategories', []); + + /* Insert current value to possibilities */ + if (isset($this->attributesAccess['businessCategory'])) { + $businessCategory = $this->attributesAccess['businessCategory']->getValue(); + if (($businessCategory != "") && !in_array($businessCategory, $categoriesList)) { + $categoriesList[] = $businessCategory; + } + $this->attributesAccess['businessCategory']->setChoices($categoriesList); + } + + $this->attributesAccess[static::$namingAttr]->setUnique('one'); + } + + /* Check values */ + function check (): array + { + $errors = parent::check(); + + $namingAttr = static::$namingAttr; + + if (($namingAttr == 'ou') && tests::is_department_name_reserved($this->$namingAttr)) { + $errors[] = new SimplePluginCheckError( + $this, + msgPool::reserved(_('Name')) + ); + } + + return $errors; + } + + function get_allowed_bases (): array + { + /* Hide all departments, that are subtrees of this department */ + $bases = parent::get_allowed_bases(); + if (($this->dn != "new") && ($this->dn != "")) { + foreach (array_keys($bases) as $dn) { + if (preg_match("/".preg_quote($this->dn)."/", $dn)) { + unset($bases[$dn]); + } + } + } + return $bases; + } + + function prepare_save (): array + { + if (static::$namingAttr != 'ou') { + $this->attributesAccess['ou']->setValue($this->attributesAccess[static::$namingAttr]->getValue()); + } + return parent::prepare_save(); + } +} diff --git a/plugins/management/departments/class_departmentManagement.inc b/plugins/management/departments/class_departmentManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..aaa8764dc8ca149ab75b6022c40975c46e21133a --- /dev/null +++ b/plugins/management/departments/class_departmentManagement.inc @@ -0,0 +1,79 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2018 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class departmentManagement extends management +{ + public static $skipSnapshots = TRUE; + + public static function plInfo (): array + { + return [ + 'plShortName' => _('Departments'), + 'plTitle' => _('Manage departments'), + 'plDescription' => _('Manage departments, countries, domain components, domains, localities and organization nodes,'), + 'plIcon' => 'geticon.php?context=places&icon=folder&size=48', + 'plSection' => ['accounts' => ['name' => _('Users and groups'), 'priority' => 0]], + 'plPriority' => 0, + 'plManages' => static::getDepartmentTypes(), + + 'plProvidedAcls' => [] + ]; + } + + // Overriden save handler - We've to take care about the department tagging here. + function saveChanges () + { + $str = parent::saveChanges(); + if (!empty($str)) { + return $str; + } + + $this->refreshDeps(); + } + + function refreshDeps () + { + global $config, $ui; + $config->resetDepartmentCache(); + $ui->reset_acl_cache(); + $this->listing->refreshBasesList(); + } + + // Overridden remove request method - Avoid removal of the ldap base. + public function removeRequested (array $action) + { + global $config; + $action['targets'] = array_remove_entries([$config->current['BASE']], $action['targets']); + return parent::removeRequested($action); + } + + // Finally remove departments and update department browsers + public function removeConfirmed (array $action) + { + parent::removeConfirmed($action); + $this->refreshDeps(); + } + + static function getDepartmentTypes (): array + { + return ['DEPARTMENT','COUNTRY','DCOBJECT','DOMAIN','LOCALITY','ORGANIZATION']; + } +} diff --git a/plugins/management/departments/class_domain.inc b/plugins/management/departments/class_domain.inc new file mode 100644 index 0000000000000000000000000000000000000000..85d7a2813a648ea79f0f755f86f9009fd8d4fe0c --- /dev/null +++ b/plugins/management/departments/class_domain.inc @@ -0,0 +1,56 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class domain extends department +{ + static protected $namingAttr = 'dc'; + protected $structuralOC = ['domain']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Domain'), + 'plDescription' => _('Domain'), + 'plObjectClass' => ['domain', 'gosaDepartment'], + 'plObjectType' => ['domain' => [ + 'aclCategory' => 'department', + 'name' => _('Domain'), + 'ou' => '', + 'icon' => 'geticon.php?context=types&icon=domain&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ]], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + $attributesInfo = parent::getDepartmentAttributesInfo(_('domain')); + unset($attributesInfo['location']); + unset($attributesInfo['properties']['attrs'][3]); // category + return $attributesInfo; + } +} diff --git a/plugins/management/departments/class_locality.inc b/plugins/management/departments/class_locality.inc new file mode 100644 index 0000000000000000000000000000000000000000..b479f728a4711acb131199e3d1a063a6f3055748 --- /dev/null +++ b/plugins/management/departments/class_locality.inc @@ -0,0 +1,57 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class locality extends department +{ + static protected $namingAttr = 'l'; + protected $structuralOC = ['locality']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Locality'), + 'plDescription' => _('Locality'), + 'plObjectClass' => ['gosaDepartment'], + 'plFilter' => '(&(!(objectClass=dcObject))(objectClass=locality)(objectClass=gosaDepartment))', + 'plObjectType' => ['locality' => [ + 'aclCategory' => 'department', + 'name' => _('Locality'), + 'ou' => '', + 'icon' => 'geticon.php?context=types&icon=locality&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ]], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + $attributesInfo = parent::getDepartmentAttributesInfo(_('locality')); + unset($attributesInfo['location']); + unset($attributesInfo['properties']['attrs'][3]); // category + return $attributesInfo; + } +} diff --git a/plugins/management/departments/class_organization.inc b/plugins/management/departments/class_organization.inc new file mode 100644 index 0000000000000000000000000000000000000000..5b8cab3acf2f51053c58a27ba6e00a47caa6e12b --- /dev/null +++ b/plugins/management/departments/class_organization.inc @@ -0,0 +1,54 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2013-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class organization extends department +{ + static protected $namingAttr = 'o'; + protected $structuralOC = ['organization']; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Organization'), + 'plDescription' => _('Organization'), + 'plObjectClass' => ['gosaDepartment'], + 'plFilter' => '(&(objectClass=organization)(objectClass=gosaDepartment))', + 'plObjectType' => ['organization' => [ + 'aclCategory' => 'department', + 'name' => _('Organization'), + 'ou' => '', + 'icon' => 'geticon.php?context=types&icon=organization&size=16', + 'tabClass' => 'deptabs', + 'mainAttr' => static::$namingAttr, + ]], + 'plForeignKeys' => [ + 'manager' => ['user','dn','manager=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return parent::getDepartmentAttributesInfo(_('organization')); + } +} diff --git a/plugins/management/departments/tabs_department.inc b/plugins/management/departments/tabs_department.inc new file mode 100644 index 0000000000000000000000000000000000000000..2f1d728c16a02ed6c639783dbda3b996f4bb5440 --- /dev/null +++ b/plugins/management/departments/tabs_department.inc @@ -0,0 +1,34 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class deptabs extends simpleTabs +{ + function save () + { + $errors = parent::save(); + + /* Update department cache */ + global $config; + $config->resetDepartmentCache(); + + return $errors; + } +} diff --git a/plugins/management/groups/class_GroupContentColumn.inc b/plugins/management/groups/class_GroupContentColumn.inc new file mode 100644 index 0000000000000000000000000000000000000000..1731c6274e87a4933e6a7c64ef0a93e5129ebc0e --- /dev/null +++ b/plugins/management/groups/class_GroupContentColumn.inc @@ -0,0 +1,56 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2017-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/*! + * \brief Column showing the types of the member objects + */ +class GroupContentColumn extends Column +{ + function renderCell (ListingEntry $entry): string + { + global $config; + + if (strtolower($entry->getTemplatedType()) == 'ogroup') { + $types = preg_replace('/[^a-z]/i', '', implode('', $this->getAttributeValues($entry))); + } else { + $types = 'U'; + } + + $result = ''; + for ($i = 0; $i < strlen($types); $i++) { + if ($types[$i] == 'I') { + continue; + } + try { + $infos = objects::infos(ogroup::$objectTypes[$types[$i]]); + $result .= '<img src="'.htmlescape($infos['icon']).'"'. + ' alt="'.htmlescape($infos['name']).'"'. + ' title="'.htmlescape($infos['name']).'"/>'; + } catch (NonExistingObjectTypeException $e) { + continue; + } + } + if ($result == '') { + return ' '; + } + return $result; + } +} diff --git a/plugins/management/groups/class_GroupContentFilterElement.inc b/plugins/management/groups/class_GroupContentFilterElement.inc new file mode 100644 index 0000000000000000000000000000000000000000..e2289fcae4d3a4af7f7f90985855ccfc74b31610 --- /dev/null +++ b/plugins/management/groups/class_GroupContentFilterElement.inc @@ -0,0 +1,92 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2017-2018 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/*! + * \brief Filter on member types + */ +class GroupContentFilterElement extends FilterElement +{ + protected $types; + + public function __construct (managementFilter $parent) + { + global $config; + + parent::__construct($parent); + + $this->types = []; + foreach (ogroup::$objectTypes as $key => $type) { + try { + $infos = objects::infos($type); + $this->types[$type] = [ + 'filter' => '(gosaGroupObjects=*'.$key.'*)', + 'infos' => $infos, + 'checked' => FALSE, + ]; + } catch (NonExistingObjectTypeException $e) { + continue; + } + } + } + + public function update () + { + foreach ($this->types as $type => &$infos) { + $infos['checked'] = isset($_POST['filter_member_'.$type]); + } + unset($infos); + } + + public function render (): string + { + $inputs = []; + foreach ($this->types as $type => $infos) { + $inputs['filter_member_'.$type] = [ + 'name' => $infos['infos']['name'], + 'desc' => (isset($infos['infos']['description']) ? $infos['infos']['description'] : $infos['infos']['name']).' '.$infos['filter'], + 'icon' => (isset($infos['infos']['icon']) ? $infos['infos']['icon'] : NULL), + 'checked' => $infos['checked'], + ]; + } + $smarty = get_smarty(); + $smarty->assign('NAME', _('Members')); + $smarty->assign('INPUTS', $inputs); + return $smarty->fetch(get_template_path('management/filter-element.tpl')); + } + + public function getFilters (string $type, array &$filters): bool + { + if (strtoupper($type) == 'OGROUP') { + foreach ($this->types as $type => $infos) { + if ($infos['checked']) { + $filters[] = $infos['filter']; + } + } + } elseif (!$this->types['user']['checked']) { + /* Skip POSIX groups and roles if users are unchecked and at least one type is checked */ + foreach ($this->types as $type => $infos) { + if ($infos['checked']) { + return TRUE; + } + } + } + return FALSE; + } +} diff --git a/plugins/management/groups/class_GroupMembersAttribute.inc b/plugins/management/groups/class_GroupMembersAttribute.inc new file mode 100644 index 0000000000000000000000000000000000000000..e2aac3c29a1ec9ff19b6f3db44ce196425961412 --- /dev/null +++ b/plugins/management/groups/class_GroupMembersAttribute.inc @@ -0,0 +1,98 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2020 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class GroupMembersAttribute extends ObjectsAttribute +{ + protected $typeToCodeMap; + protected $childTypes = []; + + function __construct ($label, $description, $ldapName, $required = FALSE, $defaultValue = [], $store_attr = 'dn', $display_attr = 'cn', array $filterElementDefinitions = NULL, $acl = '') + { + parent::__construct($label, $description, $ldapName, $required, array_values(ogroup::$objectTypes), $defaultValue, $store_attr, $display_attr, $filterElementDefinitions, $acl); + $this->typeToCodeMap = array_flip(array_map('strtolower', ogroup::$objectTypes)); + $this->selectManagementParameters[2]['gosaGroupObjects'] = '*'; + } + + function getFilterBlackList () + { + return [ + 'dn' => array_merge($this->getValue(), $this->plugin->used_workstations, [$this->plugin->dn]) + ]; + } + + protected function typeToCode ($type): string + { + if ($type === FALSE) { + return 'I'; + } else { + return $this->typeToCodeMap[strtolower($type)]; + } + } + + function listObjectTypes () + { + /* Refresh types and displays */ + $this->getDisplayValues(); + /* Merge all object types codes and all child types */ + $codes = array_unique( + array_merge( + array_map([$this, 'typeToCode'], array_unique($this->types)), + str_split(preg_replace('/[^a-z]/i', '', implode('', $this->childTypes))) + ) + ); + sort($codes); + return '['.implode('', $codes).']'; + } + + function getTypedValues () + { + $values = $this->getValue(); + $ret = []; + foreach ($values as $i => $v) { + $code = $this->typeToCode($this->types[$i]); + if (!isset($ret[$code])) { + $ret[$code] = []; + } + $ret[$code][] = $v; + } + + return $ret; + } + + protected function fillDisplayValueFrom ($i, $attrs) + { + parent::fillDisplayValueFrom($i, $attrs); + $this->childTypes[$i] = ($attrs['gosaGroupObjects'][0] ?? ''); + } + + function setValue ($value) + { + $this->childTypes = []; + parent::setValue($value); + } + + protected function removeValue ($row) + { + parent::removeValue($row); + unset($this->childTypes[$row]); + } +} diff --git a/plugins/management/groups/class_groupManagement.inc b/plugins/management/groups/class_groupManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..816cead59daccc78596a7a47cef4e4d45ac6da02 --- /dev/null +++ b/plugins/management/groups/class_groupManagement.inc @@ -0,0 +1,244 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class groupManagement extends management +{ + public static $skipTemplates = FALSE; + + public static $columns = [ + ['ObjectTypeColumn', []], + ['LinkColumn', ['attributes' => 'cn', 'label' => 'Name']], + ['LinkColumn', ['attributes' => 'description', 'label' => 'Description']], + ['GroupContentColumn', ['attributes' => 'gosaGroupObjects', 'label' => 'Members']], + ['PropertiesColumn', ['label' => 'Properties']], + ['ActionsColumn', ['label' => 'Actions']], + ]; + + protected $siActive = FALSE; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Groups and roles'), + 'plTitle' => _('Manage groups and roles'), + 'plDescription' => _('Allows you to manage object groups, POSIX groups and roles'), + 'plIcon' => 'geticon.php?context=types&icon=resource-group&size=48', + 'plSection' => 'accounts', + 'plManages' => ['ogroup', 'group', 'role', 'dyngroup'], + 'plPriority' => 20, + + 'plProvidedAcls' => [] + ]; + } + + function __construct () + { + // Check if we are able to communicate with Argonaut server + if (class_available('supportDaemon') && class_available('argonautAction')) { + $o = new supportDaemon(); + $this->siActive = $o->is_available(); + } + + parent::__construct( + FALSE, + [ + ['TabFilterElement', []], + ['GroupContentFilterElement', []], + ] + ); + } + + protected function configureActions () + { + parent::configureActions(); + + if ($this->siActive) { + $triggerActions = []; + $scheduleActions = []; + $events = argonautEventTypes::get_event_types(); + foreach ($events as $name => $infos) { + $triggerActions[] = new Action( + 'trigger_'.$name, $infos['name'], $infos['img'], + '*', 'handleEvent' + ); + $scheduleActions[] = new Action( + 'schedule_'.$name, $infos['name'], $infos['img'], + '*', 'handleEvent' + ); + } + $this->registerAction( + new SubMenuAction( + 'trigger', _('Trigger action'), 'geticon.php?context=types&icon=action&size=16', + $triggerActions + ) + ); + $this->registerAction( + new SubMenuAction( + 'schedule', _('Schedule action'), 'geticon.php?context=actions&icon=task-schedule&size=16', + $scheduleActions + ) + ); + $this->actions['trigger']->setSeparator(TRUE); + $this->registerAction(new HiddenAction('saveEvent', 'saveEventDialog')); + } + } + + /*! \brief Handle Argonaut events + * All schedules and triggered events are handled here. + */ + function handleEvent ($action) + { + global $config; + + if (!$this->siActive) { + return; + } + + // Detect whether this event is scheduled or triggered. + $triggered = ($action['action'] == 'trigger'); + $event = $action['subaction']; + + // Now send FAI/Argonaut events here. + $mac = []; + + // Collect target mac addresses + $ldap = $config->get_ldap_link(); + foreach ($action['targets'] as $dn) { + $obj = $this->listing->getEntry($dn); + if (isset($obj['member'])) { + foreach ($obj['member'] as $member) { + $ldap->cat($member, ['macAddress']); + if ($attrs = $ldap->fetch()) { + if (isset($attrs['macAddress'][0])) { + $mac[] = $attrs['macAddress'][0]; + } else { + $error = new FusionDirectoryError( + htmlescape(sprintf( + _('System %s has no mac address defined, cannot trigger action'), + $member + )) + ); + $error->display(); + } + } else { + $error = new FusionDirectoryError( + htmlescape(sprintf( + _('Could not find system %s, cannot trigger action'), + $member + )) + ); + $error->display(); + } + } + } + } + + if ((count($mac) == 0) && $triggered) { + return; + } + + $o_queue = new supportDaemon(); + + /* Skip installation or update trigerred events, + * if this entry is currently processing. + */ + if ($triggered && in_array($event, ['reinstall','update'])) { + foreach ($mac as $key => $mac_address) { + if ($o_queue->is_currently_installing($mac_address)) { + $error = new FusionDirectoryError( + htmlescape(sprintf( + _('System %s is currently installing, cannot trigger action'), + $dn + )) + ); + $error->display(); + unset($mac[$key]); + } + } + } + + if ((count($mac) == 0) && $triggered) { + return; + } + + // Prepare event to be added + $events = argonautEventTypes::get_event_types(); + if (isset($events[$event])) { + $this->dialogObject = new argonautAction($this, $event, $mac, !$triggered); + + if ($triggered) { + $res = $o_queue->append($this->dialogObject); + if ($o_queue->is_error()) { + $error = new FusionDirectoryError(msgPool::siError($o_queue->get_error())); + $error->display(); + } else { + if (is_array($res) && count($res) > 1) { + msg_dialog::display(_('Action triggered'), htmlescape(sprintf(_('Action called without error (results were "%s")'), implode(', ', $res))), INFO_DIALOG); + } else { + if (is_array($res)) { + $res = $res[0]; + } + msg_dialog::display(_('Action triggered'), htmlescape(sprintf(_('Action called without error (result was "%s")'), $res)), INFO_DIALOG); + } + } + $this->closeDialogs(); + } + } + } + + /*! \brief Save event dialogs. + * And append the new Argonaut event. + */ + function saveEventDialog () + { + $this->dialogObject->readPost(); + $this->dialogObject->update(); + $msgs = $this->dialogObject->check(); + if (count($msgs)) { + msg_dialog::displayChecks($msgs); + return; + } + if ($this->siActive) { + $o_queue = new supportDaemon(); + $o_queue->append($this->dialogObject); + if ($o_queue->is_error()) { + $error = new FusionDirectoryError(msgPool::siError($o_queue->get_error())); + $error->display(); + } + $this->closeDialogs(); + } + } + + /*! \brief Detects actions/events send by the ui + * and the corresponding targets. + */ + function detectPostActions (): array + { + $action = parent::detectPostActions(); + if (isset($_POST['save_event_dialog'])) { + $action['action'] = 'saveEvent'; + } elseif (isset($_POST['abort_event_dialog'])) { + $action['action'] = 'cancel'; + } + return $action; + } +} diff --git a/plugins/management/groups/class_ogroup.inc b/plugins/management/groups/class_ogroup.inc new file mode 100644 index 0000000000000000000000000000000000000000..9dc7599765e4ca00f47e58c4b72438698d30e1d6 --- /dev/null +++ b/plugins/management/groups/class_ogroup.inc @@ -0,0 +1,272 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2020 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class ogroup extends simplePlugin +{ + var $used_workstations = []; + + protected $savedTypedMembers; + + static $objectTypes = [ + 'U' => 'user', + 'G' => 'ogroup', + 'A' => 'application', + 'S' => 'server', + 'W' => 'workstation', + 'T' => 'terminal', + 'F' => 'phone', + 'P' => 'printer', + 'D' => 'simpleSecurityObject', + ]; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Object group'), + 'plDescription' => _('Object group information'), + 'plPriority' => 1, + 'plObjectClass' => ['groupOfNames', 'gosaGroupOfNames'], + 'plFilter' => '(objectClass=groupOfNames)', + 'plObjectType' => ['ogroup' => [ + 'name' => _('Group'), + 'description' => _('Group'), + 'ou' => get_ou('ogroupRDN'), + 'icon' => 'geticon.php?context=types&icon=resource-group&size=16', + 'tabClass' => 'ogrouptabs', + ]], + 'plForeignKeys' => [ + 'member' => [ + ['user', 'dn','member=%oldvalue%','*'], + ['ogroup', 'dn','member=%oldvalue%','*'], + ['application', 'dn','member=%oldvalue%','*'], + ['serverGeneric', 'dn','member=%oldvalue%','*'], + ['workstationGeneric', 'dn','member=%oldvalue%','*'], + ['terminalGeneric', 'dn','member=%oldvalue%','*'], + ['phoneGeneric', 'dn','member=%oldvalue%','*'], + ['printGeneric', 'dn','member=%oldvalue%','*'], + ], + 'owner' => [ + ['user','dn','owner=%oldvalue%','*'] + ] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + 'main' => [ + 'name' => _('Properties'), + 'attrs' => [ + new BaseSelectorAttribute(get_ou('ogroupRDN')), + new StringAttribute( + _('Name'), _('Name of this group'), + 'cn', TRUE, + '', '', (strict_uid_mode() ? '/^[a-z0-9_-]+$/' : '/^[a-z0-9_.-]+$/i') + ), + new TextAreaAttribute( + _('Description'), _('Short description of this group'), + 'description', FALSE + ), + new HiddenAttribute('gosaGroupObjects'), + new UserAttribute( + _('Owner'), _('Owner'), + 'owner', FALSE + ), + ] + ], + 'members' => [ + 'name' => _('Member objects'), + 'attrs' => [ + new GroupMembersAttribute( + '', _('Objects member of this group'), + 'member', TRUE, + [], 'dn' + ) + ] + ], + 'system_trust' => [ + 'name' => _('System trust'), + 'icon' => 'geticon.php?context=status&icon=locked&size=16', + 'attrs' => [ + new SelectAttribute( + _('Trust mode'), _('Type of authorization for those hosts'), + 'trustMode', FALSE, + ['', 'fullaccess', 'byhost'], + '', + [_('disabled'), _('full access'), _('allow access to these hosts')] + ), + new SystemsAttribute( + '', _('Only allow this group to connect to this list of hosts'), + 'host', FALSE + ) + ] + ] + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['trustMode']->setInLdap(FALSE); + $this->attributesAccess['trustMode']->setManagedAttributes( + [ + 'multiplevalues' => ['notbyhost' => ['','fullaccess']], + 'erase' => [ + 'notbyhost' => ['host'] + ] + ] + ); + if ((count($this->host) == 1) && ($this->host[0] == '*')) { + $this->trustMode = 'fullaccess'; + } elseif (count($this->host) > 0) { + $this->trustMode = 'byhost'; + } + + /* Detect all workstations, which are already assigned to an object group + - Those objects will be hidden in the add object dialog. + - Check() will complain if such a system is assigned to this object group.*/ + $this->used_workstations = []; + try { + $ws_dns = array_keys(objects::ls(['workstation','terminal'])); + $res = objects::ls('ogroup', ['member' => '*'], NULL, '(&(member=*)(|(gosaGroupObjects=[W])(gosaGroupObjects=[T])))'); + } catch (NonExistingObjectTypeException $e) { + /* If workstation/terminal objectType is not existing, systems plugin is missing so there are no systems */ + $ws_dns = []; + $res = []; + } + foreach ($res as $odn => $og) { + if ($odn == $this->dn) { + continue; + } + $this->used_workstations = array_merge($this->used_workstations, array_intersect($ws_dns, $og['member'])); + } + + $this->reload(); + $this->savedTypedMembers = $this->attributesAccess['member']->getTypedValues(); + } + + function prepare_save (): array + { + $this->reload(); + $errors = parent::prepare_save(); + + if ($this->trustMode == 'fullaccess') { + $this->attrs['host'] = ['*']; + } + + /* Trust accounts */ + if (($this->trustMode != "") && !in_array('hostObject', $this->attrs['objectClass'])) { + $this->attrs['objectClass'][] = 'hostObject'; + } elseif (($this->trustMode == "") && (($key = array_search('hostObject', $this->attrs['objectClass'])) !== FALSE)) { + unset($this->attrs['objectClass'][$key]); + } + + return $errors; + } + + function reload () + { + $this->gosaGroupObjects = $this->attributesAccess['member']->listObjectTypes(); + } + + function check (): array + { + $errors = parent::check(); + $this->reload(); + if (preg_match('/W/', $this->gosaGroupObjects) && preg_match('/T/', $this->gosaGroupObjects)) { + $errors[] = new SimplePluginCheckError( + $this, + htmlescape(_('Putting both workstations and terminals in the same group is not allowed')) + ); + } + + return $errors; + } + + function ldap_save (): array + { + global $ui; + + $errors = []; + + if (isset($this->attrs['member'])) { + $userMembers = []; + $savedMembers = []; + $members = $this->attributesAccess['member']->getTypedValues(); + if (isset($members['U'])) { + $userMembers = $members['U']; + } + if (isset($this->savedTypedMembers['U'])) { + $savedMembers = $this->savedTypedMembers['U']; + } + $addingMembers = array_diff($userMembers, $savedMembers); + $removingMembers = array_diff($savedMembers, $userMembers); + foreach ($addingMembers as $dn) { + if (strpos($ui->get_permissions($dn, 'user/userRoles', 'groupsMembership', $this->acl_skip_write()), 'w') === FALSE) { + $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'groupsMembership')); + } + } + foreach ($removingMembers as $dn) { + if (strpos($ui->get_permissions($dn, 'user/userRoles', 'groupsMembership', $this->acl_skip_write()), 'w') === FALSE) { + $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'groupsMembership')); + } + } + } + + if (!empty($errors)) { + return $errors; + } + + return parent::ldap_save(); + } + + function handleForeignKeys (string $olddn = NULL, string $newdn = NULL, string $mode = 'move') + { + if (($olddn !== NULL) && ($olddn == $newdn)) { + return; + } + if ($this->is_template) { + return; + } + parent::handleForeignKeys($olddn, $newdn, $mode); + if ($this->attributeHaveChanged('gosaGroupObjects')) { + /* Propagate member type changes to parent groups */ + $parents = objects::ls('ogroup', ['dn' => 'raw'], NULL, '(member='.ldap_escape_f($this->dn).')'); + foreach (array_keys($parents) as $dn) { + $tabobject = objects::open($dn, 'ogroup'); + $errors = $tabobject->save(); + msg_dialog::displayChecks($errors); + } + } + } + + function getGroupObjectTypes () + { + $this->reload(); + return $this->gosaGroupObjects; + } +} diff --git a/plugins/management/groups/class_roleGeneric.inc b/plugins/management/groups/class_roleGeneric.inc new file mode 100644 index 0000000000000000000000000000000000000000..399d0d6968f9b0516e7c918b79634a4b473ae69f --- /dev/null +++ b/plugins/management/groups/class_roleGeneric.inc @@ -0,0 +1,144 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class RoleMembersAttribute extends UsersAttribute +{ + protected $whitelistDns = NULL; + + function getFilterWhiteList () + { + global $config; + if ($config->get_cfg_value('RestrictRoleMembers') == 'TRUE') { + if ($this->whitelistDns === NULL) { + /* Computes a list of members of all groups within our branch */ + $groups = objects::ls('ogroup', ['member' => '*'], $this->plugin->base); + if (empty($groups)) { + $this->whitelistDns = []; + } else { + $this->whitelistDns = array_merge_recursive(...array_values($groups))['member']; + } + } + return [ + 'branches' => [$this->plugin->base], + 'dn' => $this->whitelistDns, + ]; + } else { + return []; + } + } +} + +class roleGeneric extends simplePlugin +{ + static function plInfo (): array + { + return [ + 'plShortName' => _('Role'), + 'plDescription' => _('Role information'), + 'plObjectClass' => ['organizationalRole'], + 'plFilter' => '(&(objectClass=organizationalRole)(!(objectClass=simpleSecurityObject)))', + 'plObjectType' => ['role' => + [ + 'name' => _('Role'), + 'description' => _('Organizational role'), + 'ou' => get_ou('roleRDN'), + 'icon' => 'geticon.php?context=types&icon=role&size=16', + 'mainAttr' => 'cn', + ] + ], + 'plForeignKeys' => [ + 'roleOccupant' => ['user','dn','roleOccupant=%oldvalue%','*'] + ], + 'plSearchAttrs' => ['description'], + + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + /*! + * \brief The main function : information about attributes + */ + static function getAttributesInfo (): array + { + return [ + 'main' => [ + 'name' => _('Information'), + 'attrs' => [ + new BaseSelectorAttribute(get_ou('roleRDN')), + new StringAttribute( + _('Name'), _('Name of this group'), + 'cn', TRUE, + '', '', (strict_uid_mode() ? '/^[a-z0-9_-]+$/i' : '/^[a-z0-9_.-]+$/i') + ), + new StringAttribute( + _('Description'), _('Description of the role'), + 'description' + ), + new StringAttribute( + _('Phone number'), _('Phone number'), + 'telephoneNumber' + ), + new StringAttribute( + _('Fax number'), _('Fax number'), + 'facsimileTelephoneNumber' + ) + ] + ], + 'members' => [ + 'name' => _('Role members'), + 'attrs' => [ + new RoleMembersAttribute( + '', _('Add users for the role'), + 'roleOccupant', FALSE + ) + ] + ] + ]; + } + + function ldap_save (): array + { + global $ui; + + $errors = []; + + if (isset($this->attrs['roleOccupant'])) { + $savedOccupants = $this->attributesAccess['roleOccupant']->getInitialValue(); + $addingOccupants = array_diff($this->roleOccupant, $savedOccupants); + $removingOccupants = array_diff($savedOccupants, $this->roleOccupant); + foreach ($addingOccupants as $dn) { + if (strpos($ui->get_permissions($dn, 'user/userRoles', 'rolesMembership', $this->acl_skip_write()), 'w') === FALSE) { + $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'rolesMembership')); + } + } + foreach ($removingOccupants as $dn) { + if (strpos($ui->get_permissions($dn, 'user/userRoles', 'rolesMembership', $this->acl_skip_write()), 'w') === FALSE) { + $errors[] = new SimplePluginPermissionError($this, msgPool::permModify($dn, 'rolesMembership')); + } + } + } + + if (!empty($errors)) { + return $errors; + } + + return parent::ldap_save(); + } +} diff --git a/plugins/management/groups/tabs_ogroups.inc b/plugins/management/groups/tabs_ogroups.inc new file mode 100644 index 0000000000000000000000000000000000000000..f600dd382da1a1ff6d5c0afd6d0b685380331fa4 --- /dev/null +++ b/plugins/management/groups/tabs_ogroups.inc @@ -0,0 +1,217 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2016 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class ogrouptabs extends simpleTabs_noSpecial +{ + protected $groupObjects = NULL; + protected $removed_tabs = []; + + function __construct ($type, $dn, $copied_object = NULL) + { + parent::__construct($type, $dn, $copied_object); + + $this->loadTabs($this->getBaseObject()->gosaGroupObjects); + } + + public function loadTabs (string $gosaGroupObjects = NULL) + { + global $config; + + if ($gosaGroupObjects === NULL) { + $gosaGroupObjects = $this->getBaseObject()->getGroupObjectTypes(); + } + + if ($this->groupObjects == $gosaGroupObjects) { + return; + } + $this->groupObjects = $gosaGroupObjects; + $objects = preg_replace('/[\[\]]/', '', $gosaGroupObjects); + + $tabs = []; + if (strlen($objects)) { + if (isset($config->data['TABS']['OGROUP-DYNAMICTABS'])) { + $dtabs = $config->data['TABS']['OGROUP-DYNAMICTABS']; + } else { + $dtabs = []; + } + $tabs = FALSE; + for ($i = 0; $i < strlen($objects); $i++) { + if (in_array($objects[$i], ['I','G'])) { + /* Ignore unknown objects and groups */ + continue; + } + $otype = ogroup::$objectTypes[$objects[$i]]; + $otabs = []; + if (isset($config->data['TABS'][strtoupper($otype).'TABS'])) { + $otabs = array_uintersect( + $dtabs, + $config->data['TABS'][strtoupper($otype).'TABS'], + function ($a, $b) + { + return strcmp($a['CLASS'], $b['CLASS']); + } + ); + } + if (isset($config->data['TABS']['OGROUP-'.strtoupper($otype).'TABS'])) { + $otabs = array_merge( + $otabs, + $config->data['TABS']['OGROUP-'.strtoupper($otype).'TABS'] + ); + } + if ($tabs === FALSE) { + $tabs = $otabs; + } else { + $tabs = array_uintersect( + $tabs, + $otabs, + function ($a, $b) + { + return strcmp($a['CLASS'], $b['CLASS']); + } + ); + } + } + } + $tabs = array_merge($config->data['TABS']['OGROUPTABS'], (array)$tabs); + $tabs = array_map( + function ($a) + { + return $a['CLASS']; + }, + $tabs + ); + + foreach ($this->by_object as $tab => $object) { + if (!in_array($tab, $tabs)) { + $this->delTab($tab); + } + } + foreach ($tabs as $class) { + if (class_available($class) && !isset($this->by_object["$class"])) { + $this->addTab($class); + } + } + $this->addSpecialTabs(); + } + + function addTab ($class) + { + logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $class, 'Adding tab '); + $plInfos = pluglist::pluginInfos($class); + $this->by_name[$class] = $plInfos['plShortName']; + $this->plNotify[$class] = FALSE; + if (isset($this->removed_tabs[$class])) { + $this->by_object[$class] = $this->removed_tabs[$class]; + unset($this->removed_tabs[$class]); + } else { + $this->by_object[$class] = new $class($this->dn, $this->getBaseObject(), $this, FALSE); + $this->by_object[$class]->set_acl_category($this->acl_category); + } + } + + function delTab ($class) + { + $this->removed_tabs["$class"] = $this->by_object["$class"]; + unset($this->by_object["$class"]); + unset($this->by_name["$class"]); + unset($this->plNotify["$class"]); + } + + protected function renderTabList (bool $disabled = FALSE): string + { + $this->loadtabs(); + return parent::renderTabList($disabled); + } + + /*! + * \brief Returns the list of tabs which may appear for a given object type + */ + public static function getPotentialTabList (string $type, array $infos): array + { + global $config; + + /* prefix will be OGROUP- for ogroup */ + $tabList = parent::getPotentialTabList($type, $infos); + $prefix = strtoupper($type).'-'; + foreach ($config->data['TABS'] as $tabgroup => $tabs) { + if (strpos($tabgroup, $prefix) === 0) { + $tabList = array_merge($tabList, $tabs); + } + } + return $tabList; + } + + /*! + * \brief Sets the active tabs from this instance to an other one. Used by templates + */ + public function setActiveTabs (&$tabObject) + { + $tabObject->loadTabs($this->groupObjects); + foreach ($this->by_object as $class => $plugin) { + if ($plugin->isActive()) { + $tabObject->by_object[$class]->is_account = $plugin->is_account; + } + } + } + + function save () + { + $errors = parent::save(); + + foreach ($this->removed_tabs as $key => &$obj) { + if (isset($this->by_object[$key])) { + continue; + } + logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $key, 'Removing'); + + $obj->dn = $this->dn; + $tabErrors = $obj->remove(FALSE); + if (!empty($tabErrors)) { + $errors = array_merge($errors, $tabErrors); + } + } + unset($obj); + + return $errors; + } + + public function update (): bool + { + parent::update(); + + /* Update reference, transfer variables */ + $baseobject = $this->getBaseObject(); + foreach ($this->by_object as $name => $obj) { + + /* Don't touch base object */ + if ($name != 'ogroup') { + $obj->uid = $baseobject->cn; + $obj->cn = $baseobject->cn; + $obj->sn = $baseobject->cn; + $obj->givenName = $baseobject->cn; + + $this->by_object[$name] = $obj; + } + } + + return TRUE; + } +} diff --git a/plugins/management/mailtemplate/.class_mailTemplateManagement.inc.swp b/plugins/management/mailtemplate/.class_mailTemplateManagement.inc.swp new file mode 100644 index 0000000000000000000000000000000000000000..129eae9eea882410d9f5b607b915db5d4ddd53ed Binary files /dev/null and b/plugins/management/mailtemplate/.class_mailTemplateManagement.inc.swp differ diff --git a/plugins/management/mailtemplate/class_mailTemplateManagement.inc b/plugins/management/mailtemplate/class_mailTemplateManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..cfbcd572d98b735b3f4b089df4c3d5cf56014a3b --- /dev/null +++ b/plugins/management/mailtemplate/class_mailTemplateManagement.inc @@ -0,0 +1,159 @@ +<?php + +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2011-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class mailTemplateManagement extends management +{ + + public static $columns = [ + ['ObjectTypeColumn', []], + ['LinkColumn', ['attributes' => 'cn', 'label' => 'Template Name']], + ['ActionsColumn', ['label' => 'Actions']], + ]; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Mail Template'), + 'plTitle' => _('Mail Template'), + 'plDescription' => _('Manages mails templates'), + 'plIcon' => 'geticon.php?context=applications&icon=mail-template&size=48', + 'plManages' => ['mailTemplate'], + 'plSection' => 'conf', + 'plPriority' => 30, + 'plProvidedAcls' => [], + ]; + } + + protected function configureActions () + { + global $config; + + $createMenu = []; + + foreach ($this->objectTypes as $type) { + $infos = objects::infos($type); + $img = 'geticon.php?context=actions&icon=document-new&size=16'; + if (isset($infos['icon'])) { + $img = $infos['icon']; + } + $createMenu[] = new Action( + 'new_'.$type, $infos['name'], $img, + '0', 'newEntry', + [$infos['aclCategory'].'/'.$infos['mainTab'].'/c'] + ); + } + + $this->registerAction( + new SubMenuAction( + 'new', _('Create'), 'geticon.php?context=actions&icon=document-new&size=16', + $createMenu + ) + ); + + // Add export actions + $exportMenu = []; + foreach ($this->exporters as $action => $exporter) { + $exportMenu[] = new Action( + $action, $exporter['label'], $exporter['image'], + '0', 'export' + ); + } + $this->registerAction( + new SubMenuAction( + 'export', _('Export list'), 'geticon.php?context=actions&icon=document-export&size=16', + $exportMenu + ) + ); + + $this->registerAction( + new Action( + 'edit', _('Edit'), 'geticon.php?context=actions&icon=document-edit&size=16', + '+', 'editEntry' + ) + ); + $this->actions['edit']->setSeparator(TRUE); + + if (!$this->skipCpHandler) { + $this->registerAction( + new Action( + 'cut', _('Cut'), 'geticon.php?context=actions&icon=edit-cut&size=16', + '+', 'copyPasteHandler', + ['dr'] + ) + ); + $this->registerAction( + new Action( + 'copy', _('Copy'), 'geticon.php?context=actions&icon=edit-copy&size=16', + '+', 'copyPasteHandler', + ['r'] + ) + ); + $this->registerAction( + new Action( + 'paste', _('Paste'), 'geticon.php?context=actions&icon=edit-paste&size=16', + '0', 'copyPasteHandler', + ['w'] + ) + ); + $this->actions['paste']->setEnableFunction([$this, 'enablePaste']); + } + + $this->registerAction( + new Action( + 'remove', _('Remove'), 'geticon.php?context=actions&icon=edit-delete&size=16', + '+', 'removeRequested', + ['d'] + ) + ); + + if (!static::$skipSnapshots && ($config->get_cfg_value('enableSnapshots') == 'TRUE')) { + $this->registerAction( + new Action( + 'snapshot', _('Create snapshot'), 'geticon.php?context=actions&icon=snapshot&size=16', + '1', 'createSnapshotDialog', + ['/SnapshotHandler/c'] + ) + ); + $this->registerAction( + new Action( + 'restore', _('Restore snapshot'), 'geticon.php?context=actions&icon=document-restore&size=16', + '*', 'restoreSnapshotDialog', + ['w', '/SnapshotHandler/r'] + ) + ); + $this->actions['snapshot']->setSeparator(TRUE); + $this->actions['restore']->setEnableFunction([$this, 'enableSnapshotRestore']); + } + + + /* Actions from footer are not in any menus and do not need a label */ + $this->registerAction(new HiddenAction('apply', 'applyChanges')); + $this->registerAction(new HiddenAction('save', 'saveChanges')); + $this->registerAction(new HiddenAction('cancel', 'cancelEdit')); + $this->registerAction(new HiddenAction('cancelDelete', 'cancelEdit')); + $this->registerAction(new HiddenAction('removeConfirmed', 'removeConfirmed')); + if (!$this->skipConfiguration) { + $this->registerAction(new HiddenAction('configure', 'configureDialog')); + } + } + + +} diff --git a/plugins/management/tasks/class_tasksColumn.inc b/plugins/management/tasks/class_tasksColumn.inc new file mode 100644 index 0000000000000000000000000000000000000000..191daf42c45042d669c600ae6c618590761e1992 --- /dev/null +++ b/plugins/management/tasks/class_tasksColumn.inc @@ -0,0 +1,91 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2011-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/*! + * \brief Column rendering Tasks columns + */ +class TasksColumn extends Column +{ + // Keep in mind this method is being called for each value. + protected function renderSingleValue (ListingEntry $entry, string $value): string + { + if ($value == '') { + return ' '; + } else { + switch ($this->attributes[0]) { + case 'fdTasksStatus': + return static::filterStatus($value); + + case 'fdTasksGranularStatus': + return static::filterStatus($value); + + // This case needs optimization + case 'fdTasksMailObject': + return "Mail Object"; + + case 'fdTasksScheduleDate': + return static::generateDateFormat($value); + + case 'fdTasksGranularSchedule': + return static::generateDateFormat($value); + + case 'fdTasksGranularMaster': + return static::generateMasterTaskName($value); + + default: + return parent::renderSingleValue($entry, $value); + } + } + } + + static function filterStatus (string $status = NULL): string + { + // A call towards a status map would be interesting here. + switch ($status) { + case "1" : + return "Scheduled"; + + case "2" : + return "Completed"; + + default : + return $status; + } + } + + static function generateDateFormat ($value) : string + { + // Z is added to value to match ldap generaliseztime + $datetime = (new LdapGeneralizedTime)->fromString($value.'Z'); + $result = $datetime->format('Y-m-d H:i:s'); + return $result; + } + + static function generateMasterTaskName ($value) : string + { + // remove 'dn' keeping only 'cn' + $rmDn = preg_replace('/(?=,).*/', '', $value); + // only take the cn without dc + preg_match('/cn=(.*)/', $rmDn, $matches); + return $matches[1]; + } + +} diff --git a/plugins/management/users/class_LockAction.inc b/plugins/management/users/class_LockAction.inc new file mode 100644 index 0000000000000000000000000000000000000000..632a13bec98b8555165fe18c38df01cec329cfd3 --- /dev/null +++ b/plugins/management/users/class_LockAction.inc @@ -0,0 +1,103 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2018-2019 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class LockAction extends Action +{ + function __construct (string $name, string $targets, $callable, array $acl = [], bool $inmenu = TRUE, bool $inline = TRUE) + { + parent::__construct( + $name, + ['lock' => _('Lock users'), 'unlock' => _('Unlock users')], + [ + 'lock' => 'geticon.php?context=status&icon=object-locked&size=16', + 'unlock' => 'geticon.php?context=status&icon=object-unlocked&size=16', + ], + $targets, $callable, $acl, $inmenu, $inline); + } + + function isLocked (ListingEntry $entry) + { + if (isset($entry['userPassword'][0]) && preg_match('/^\{[^\}]/', $entry['userPassword'][0])) { + return (preg_match('/^[^\}]*+\}!/', $entry['userPassword'][0]) === 1); + } elseif ((strtolower($entry->type) == 'user') && empty($entry['userPassword'])) { + /* Empty lockable password */ + return FALSE; + } + return NULL; + } + + function fillMenuItems (array &$actions) + { + if (!$this->inmenu) { + return; + } + + if (!$this->hasPermission()) { + return; + } + + $actions[] = [ + 'name' => $this->name.'_lock', + 'icon' => $this->icon['lock'], + 'label' => $this->label['lock'], + 'enabled' => $this->isEnabledFor(), + 'separator' => $this->separator, + ]; + + $actions[] = [ + 'name' => $this->name.'_unlock', + 'icon' => $this->icon['unlock'], + 'label' => $this->label['unlock'], + 'enabled' => $this->isEnabledFor(), + 'separator' => FALSE, + ]; + } + + function fillRowClasses (array &$classes, ListingEntry $entry) + { + if ($this->isLocked($entry) === TRUE) { + $classes[] = 'entry-locked'; + } + } + + function renderColumnIcons (ListingEntry $entry): string + { + if (!$this->inline) { + return ''; + } + + // Skip the entry completely if there's no permission to execute it + if (!$this->hasPermission($entry)) { + return '<img src="images/empty.png" alt=" " class="center optional"/>'; + } + + $lockStatus = $this->isLocked($entry); + if ($lockStatus === NULL) { + return '<img src="images/empty.png" alt=" " class="center optional"/>'; + } elseif ($lockStatus) { + // Render + return '<input type="image" src="'.htmlescape($this->icon['lock']).'"'. + ' title="'.htmlescape($this->label['unlock']).'" alt="'.htmlescape($this->label['unlock']).'" name="listing_'.$this->name.'_unlock_'.$entry->row.'"/>'; + } else { + return '<input type="image" src="'.htmlescape($this->icon['unlock']).'"'. + ' title="'.htmlescape($this->label['lock']).'" alt="'.htmlescape($this->label['lock']).'" name="listing_'.$this->name.'_lock_'.$entry->row.'"/>'; + } + } +} diff --git a/plugins/management/users/class_userManagement.inc b/plugins/management/users/class_userManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..872358618f5da6b17b5b9df5b5f50417b3044d00 --- /dev/null +++ b/plugins/management/users/class_userManagement.inc @@ -0,0 +1,153 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2003 Cajus Pollmeier + Copyright (C) 2011-2018 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class userManagement extends management +{ + public $neededAttrs = ['userPassword' => '*']; + + public static $skipTemplates = FALSE; + + public static $columns = [ + ['ObjectTypeColumn', []], + ['LinkColumn', ['attributes' => 'sn', 'label' => 'Last name']], + ['LinkColumn', ['attributes' => 'givenName', 'label' => 'First name']], + ['Column', ['attributes' => 'uid', 'label' => 'Login']], + ['PropertiesColumn', ['label' => 'Properties']], + ['ActionsColumn', ['label' => 'Actions']], + ]; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Users'), + 'plTitle' => _('Manage users'), + 'plDescription' => _('Manage user accounts and their properties'), + 'plIcon' => 'geticon.php?context=types&icon=user&size=48', + 'plSection' => 'accounts', + 'plManages' => ['user'], + 'plPriority' => 10, + + 'plProvidedAcls' => [] + ]; + } + + + protected function configureActions () + { + parent::configureActions(); + + $this->registerAction( + new LockAction( + 'lockUsers', + '+', 'lockUsers', + ['userPassword:rw'], TRUE, TRUE + ) + ); + $this->actions['lockUsers']->setSeparator(TRUE); + } + + /* !\brief Lock/unlock multiple users. + */ + function lockUsers (array $action) + { + global $config, $ui; + logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $action, 'Lock'); + + // Filter out entries we are not allowed to modify + $disallowed = []; + $allowed = []; + foreach ($action['targets'] as $dn) { + if (strpos($ui->get_permissions($dn, 'user/user', 'userLock'), 'w') === FALSE) { + $disallowed[] = $dn; + } else { + $allowed[] = $dn; + } + } + if (count($disallowed)) { + $error = new FusionDirectoryPermissionError(msgPool::permModify($disallowed)); + $error->display(); + } + + // Try to lock/unlock the rest of the entries. + foreach ($allowed as $dn) { + // We can't lock empty passwords. + $entry = $this->listing->getEntry($dn); + + // Detect the password method and try to lock/unlock. + static::lockUser($action['subaction'], ($entry['userPassword'][0] ?? ''), $dn); + + // Small procedure in order to re-open the user in order to trigger plugins to update accordingly. + $userObject = objects::open($dn, 'user'); + $userObject->save(); + } + } + + /* !\brief Lock/unlock a user + * + * \param string $action 'lock' or 'unlock' + * \param string $pwd userPassword value + * \param string $dn dn of the LDAP node + */ + static function lockUser (string $action, string $pwd, string $dn) + { + $method = passwordMethod::get_method($pwd, $dn); + if ($method instanceof passwordMethod) { + if (!$method->is_lockable()) { + $hn = $method->get_hash_name(); + if (is_array($hn)) { + $hn = $hn[0]; + } + $error = new FusionDirectoryError( + htmlescape(sprintf( + _('Password method "%s" does not support locking. Account "%s" has not been locked!'), + $hn, + $dn + )) + ); + $error->display(); + return; + } + + $success = TRUE; + if (($action == 'lock') && !$method->is_locked($dn)) { + $success = $method->lock_account($dn); + } elseif (($action == 'unlock') && $method->is_locked($dn)) { + $success = $method->unlock_account($dn); + } + + // Check if everything went fine. + if (!$success) { + $hn = $method->get_hash_name(); + if (is_array($hn)) { + $hn = $hn[0]; + } + $error = new FusionDirectoryError( + htmlescape(sprintf( + _('Locking failed using password method "%s". Account "%s" has not been locked!'), + $hn, + $dn + )) + ); + $error->display(); + } + } + } +} diff --git a/plugins/workflow/mailtemplate/class_mailTemplate.inc b/plugins/workflow/mailtemplate/class_mailTemplate.inc new file mode 100644 index 0000000000000000000000000000000000000000..ff3906a5e9d2135e80c4a25a6cf4431b2ebdf3ef --- /dev/null +++ b/plugins/workflow/mailtemplate/class_mailTemplate.inc @@ -0,0 +1,118 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class mailTemplate extends simplePlugin +{ + var $displayHeader = FALSE; + + public static function plInfo (): array + { + return [ + 'plShortName' => _('Mail Template'), + 'plDescription' => _('Mail Template'), + 'plObjectClass' => ['fdMailTemplate'], + 'plFilter' => '(objectClass=fdMailTemplate)', + 'plObjectType' => ['mailTemplate' => [ + 'name' => _('Mail Template'), + 'ou' => get_ou('mailTemplateRDN'), + 'icon' => 'geticon.php?context=applications&icon=mail-template&size=16', + ]], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + global $config; + return [ + // Attributes are grouped by section + 'mailTemplate' => [ + 'name' => _('Mail Object'), + 'attrs' => [ + new StringAttribute( + _('Mail Template Name'), _('Mail Template Name'), + 'cn', TRUE + ), + new StringAttribute( + _('Mail Subject'), _('Mail Subject'), + 'fdMailTemplateSubject', TRUE + ), + new TextAreaAttribute( + _('Mail Template Body'), _('Text to be sent to recipient'), + 'fdMailTemplateBody', TRUE + ), + new TextAreaAttribute( + _('Mail Signature'), _('Mail Signature'), + 'fdMailTemplateSignature', FALSE + ), + new BooleanAttribute( + _('Read Receipt'), + _('Read Receipt'), + 'fdMailTemplateReadReceipt', FALSE + ), + // Management of attachments files in a sub-node ldap format. + new SubNodesAttribute( + '', _('Attachments'), + 'attachments', ['fdMailAttachments'], + [ + new StringAttribute( + _('Name'), _('The name of the attachment file'), + 'cn', TRUE + ), + new FileDownloadAttribute( + _('Attachment'), _('Import a file for this e-mail'), + 'fdMailAttachmentsContent', FALSE, '', TRUE + ), + ], + FALSE, /* no order */ + [], + TRUE /* edit enabled */ + ), + ] + ], + ]; + } + + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + parent::__construct($dn, $object, $parent, $mainTab); + + } + + protected function shouldSave (): bool + { + // Trigger a save method required due to sub nodes not being triggered by default. (Modification and suppression). + return TRUE; + } + + + function ldap_save (): array + { + global $config; + $errors = parent::ldap_save(); + $this->attributesAccess['attachments']->postLdapSave($config->get_ldap_link()); + return $errors; + } + + +} diff --git a/plugins/workflow/tasks/class_tasks.inc b/plugins/workflow/tasks/class_tasks.inc new file mode 100644 index 0000000000000000000000000000000000000000..d95aae6bdb1fcd637c8c24d1c8ff3408adca8a86 --- /dev/null +++ b/plugins/workflow/tasks/class_tasks.inc @@ -0,0 +1,294 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class tasks extends simplePlugin +{ + protected $displayHeader = FALSE; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Tasks'), + 'plDescription' => _('Tasks'), + 'plObjectClass' => ['fdTasks'], + 'plFilter' => '(objectClass=fdTasks)', + 'plPriority' => 41, + 'plObjectType' => ['tasks' => [ + 'name' => _('Tasks'), + 'ou' => get_ou('tasksRDN'), + 'icon' => 'geticon.php?context=applications&icon=tasks&size=16', + ]], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + static function getAttributesInfo (): array + { + return [ + // Attributes are grouped by section + 'tasks' => [ + 'name' => _('Tasks Generic'), + 'attrs' => [ + new StringAttribute( + _('Task Name'), _('Name for this task'), + 'cn', TRUE + ), + new DateTimeAttribute( + _('Schedule'), '', + 'fdTasksScheduleDate', FALSE + ), + + new HiddenAttribute('fdTasksStatus', TRUE, '1', '', 'Status', 'Status of the task'), + new HiddenAttribute('fdTasksLastExec', FALSE, '', '', 'LastExec', 'Last exec date'), + new HiddenAttribute('fdTasksCreationDate', TRUE, date("Y-m-d h:i:sa"), '', 'StartDate', 'Start Date And Time Of A Task'), + ] + ], + 'subTasks' => [ + 'name' => _('Creation of Sub Tasks - Starting this task'), + 'attrs' => [ + new BooleanAttribute( + _('Activate SubTasks'), _('Trigger the creation of this task and related subtasks'), + 'fdSubTasksActivation', FALSE + ), + ] + ], + 'taskSetting' => [ + 'name' => _('Advanced settings'), + 'attrs' => [ + new BooleanAttribute( + _('Only with new members'), _('Allows creation of sub-tasks for "NEW MEMBERS" only. (Case of Dynamic Group)'), + 'fdTasksUpdatable', FALSE, TRUE + ), + new BooleanAttribute( + _('Repeatable Task'), _('Set the task to be repeatable.'), + 'fdTasksRepeatable', FALSE + ), + new SelectAttribute( + _('Repeatable Schedule'), _('Select the desired schedule.'), + 'fdTasksRepeatableSchedule', FALSE, ['Yearly', 'Monthly', 'Weekly', 'Daily', 'Hourly'], 'Daily' + ), + ] + ], + ]; + } + + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, $mainTab); + + $this->attributesAccess['fdTasksRepeatable']->setManagedAttributes( + [ + 'disable' => [ + FALSE => [ + 'fdTasksRepeatableSchedule', + ] + ] + ] + ); + + $this->attributesAccess['fdSubTasksActivation']->setInLdap(FALSE); + } + + function save (): array + { + // Verification if the bool of activation is ticked and activate the last exec accordingly. + if ($this->fdSubTasksActivation === TRUE) { + $currentDateTime = date("Y-m-d H:i:s"); + $this->fdTasksLastExec = $currentDateTime; + } + return parent::save(); + } + + /** + * @param array $listOfDN + * @param string $attributeType + * @param array|NULL $attrs + * @param string $taskType + * @return void + * Note : $taskType is present to define the object name related to life cycle (core) and any new plugins added to tasks. + */ + public function createSlaveTasks (array $listOfDN, string $attributeType, array $attrs = NULL, string $taskType = ''): void + { + global $config; + $ldap = $config->get_ldap_link(); + + // Take the attribute from the other tabs - attribute cannot be null or unset by default + $schedule = $this->fdTasksScheduleDate ?? NULL; + // Verify if members can have multiple sub-tasks for that main task. + $newMemberOnly = $this->fdTasksUpdatable; + + // remove 'dn' keeping only 'cn' + $rmDn = preg_replace('/(?=,).*/', '', $this->dn); + // only take the cn without dc + preg_match('/cn=(.*)/', $rmDn, $matches); + + if (!empty($listOfDN)) { + // Condition allowing the creation of subtasks for existing members + if ($newMemberOnly === TRUE) { + $ldap->cd($config->current['BASE']); + $filter = '(&(objectClass=fdTasksGranular)(fdTasksGranularMaster=' . $this->dn . '))'; + $ldap->search($filter, [$attributeType]); + + // The while loop is important to get all info from ldap into the array. + while ($info = $ldap->fetch()) { + $subTasks[] = $info; + } + + if (!empty($subTasks)) { + // Recuperate members DN from the ldap search. + foreach ($subTasks as $subTask) { + + // For lifeCycle we check DN and for mailObject we check mail + switch ($attributeType) { + case 'fdTasksGranularDN': + $membersDN[] = $subTask['fdTasksGranularDN'][0]; + break; + case 'fdTasksGranularMail': + $membersDN[] = $subTask['fdTasksGranularMail'][0]; + break; + } + } + // Verify the DN differences and only keep those. + if (!empty($membersDN)) { + $listOfDN = array_diff($listOfDN, $membersDN); + } + // Simple re-index the array. + $listOfDN = array_values($listOfDN); + } + } + + foreach ($listOfDN as $dn) { + // Here we create the object taskGranular + $tabObject = objects::create('TasksGranular'); + + // Create a unique ID based on timestamp (Allowing duplicate subtasks for same members in case of repeat). + $timestamp = microtime(TRUE); // Get the current timestamp with microseconds + $timestamp = (string)$timestamp; // Convert the float to a string, str_replace expect array or string. + $uniqueID = str_replace(".", "_", $timestamp); // Remove . with _ for correct CN + + // Array matches come from preg_match function above with rmDn + $subTaskName = $matches[1] . '-SubTask-' . $uniqueID; + + // Define the type of the granular task based on the attribute type passed to this method. + $prepData = NULL; + switch ($attributeType) { + case 'fdTasksGranularMail': + $prepData['tasksGranular'] = [ + "fdTasksGranularMail" => $dn, + "fdTasksGranularType" => 'Mail Object', + "fdTasksGranularRef" => $attrs['ref'], + "fdTasksGranularMailFrom" => $attrs['from'], + "fdTasksGranularMailBCC" => $attrs['bcc'] + ]; + break; + case 'fdTasksGranularDN' : + $prepData['tasksGranular'] = [ + "fdTasksGranularDN" => $dn, + "fdTasksGranularType" => $taskType, + // Verification as 'ref' could potentially not be present depending on the logic of the main task. + "fdTasksGranularRef" => !empty($attrs['ref'][$dn]) ? array_values($attrs['ref'][$dn]) : [], + ]; + break; + } + + // Common attributes to be filled for object tasksGranular. + $defaultData['tasksGranular'] = [ + "cn" => $subTaskName, + "fdTasksGranularMaster" => $this->dn, + "fdTasksGranularSchedule" => $schedule, + ]; + // Simply merged the common values and the custom ones depending on the attribute type passed. + $values['tasksGranular'] = array_merge($prepData['tasksGranular'], $defaultData['tasksGranular']); + + foreach ($values as $tab => $tabvalues) { + if (!isset($tabObject->by_object[$tab])) { + echo "Error tab does not contains attributes values" . PHP_EOL; + } + $error = $tabObject->by_object[$tab]->deserializeValues($tabvalues); + if ($error !== TRUE) { + echo 'Error during deserializing' . $error . PHP_EOL; + } + + $tabObject->current = $tab; + $tabObject->update(); + $tabObject->loadTabs(); + } + $errors = $tabObject->save(); + + if (!empty($errors)) { + msg_dialog::displayChecks($errors); + } + } + } + } + + /** + * @param array $groups + * @return array + */ + public static function extractMembersFromGroups (array $groups): array + { + global $config; + + $ldap = $config->get_ldap_link(); + $listMemberDN = []; + + if (!empty($groups)) { + // Verify if the values received is a member or a group and collect the members DN + foreach ($groups as $group) { + if (strpos($group, "ou=groups") !== FALSE) { + + // Position ldap to the dn required (limit search). + $ldap->cd($group); + $filter = '(|(objectClass=groupOfUrls)(objectClass=groupOfNames))'; + $attrs = ['member']; + + $ldap->search($filter, $attrs); + $info = $ldap->fetch(); + + // Verify if the group is not empty of members + if (!empty($info['member'])) { + //unset the count from the array + unset($info['member']['count']); + foreach ($info['member'] as $memberDN) { + $listMemberDN[] = $memberDN; + } + // case of member not within a group or dyngroup + } + } else { + $listMemberDN[] = $group; //Here group is indeed a sole user + } + // Make sure no duplicate can happens, case of same member in an existing group. + $listMemberDN = array_unique($listMemberDN); + } + + // Iterate on the DN list to remove any members representing a group (members of that potential groups were extracted). + foreach ($listMemberDN as $key => $value) { + if (strpos($value, 'ou=groups') !== FALSE) { + unset($listMemberDN[$key]); + } + } + } + return $listMemberDN; + } + +} diff --git a/plugins/workflow/tasks/class_tasksGranular.inc b/plugins/workflow/tasks/class_tasksGranular.inc new file mode 100644 index 0000000000000000000000000000000000000000..8dfbdd777394e3e201d3343f0fe802f43a49b3b7 --- /dev/null +++ b/plugins/workflow/tasks/class_tasksGranular.inc @@ -0,0 +1,92 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + + Copyright (C) 2012-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class tasksGranular extends simplePlugin +{ + + static function plInfo (): array + { + return [ + 'plShortName' => _('Tasks Granular'), + 'plDescription' => _('Granular tasks management allowing details reports'), + 'plObjectClass' => ['fdTasksGranular'], + 'plFilter' => '(objectClass=fdTasksGranular)', + 'plPriority' => 41, + 'plObjectType' => ['tasksGranular' => [ + 'name' => _('TasksGranular'), + 'ou' => get_ou('tasksRDN'), + 'icon' => 'geticon.php?context=applications&icon=tasks&size=16', + ]], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()) + ]; + } + + /* + * Info : getAttributesInfo return is important for the management class. + * It is the return of that method which render the columns and data properly. + * Although not used by the interface directly, it is created by simpleTab during + * the save of mailTasks. + */ + static function getAttributesInfo (): array + { + return [ + // Attributes are grouped by section + 'tasksGranular' => [ + 'name' => _('Tasks Granular'), + 'attrs' => [ + new StringAttribute( + _('Task Granular Name'), _('Name for this task'), + 'cn', TRUE + ), + new HiddenAttribute('fdTasksGranularStatus', TRUE, '1', '', 'Status', 'Status of the task'), + new HiddenAttribute('fdTasksGranularMaster', TRUE, '', '', 'Master CN', 'Name of the Master task'), + new HiddenAttribute('fdTasksGranularType', TRUE, '', '', 'Type', 'Type of the task'), + new HiddenArrayAttribute('fdTasksGranularRef', FALSE, '', '', 'Type', 'Reference towards a required CN (mail template E.g'), + new DateTimeAttribute( + _('Schedule'), '', + 'fdTasksGranularSchedule', FALSE + ), + // Below attribute are for tasks of type lifeCycle and other tasks added as plugin + new StringAttribute( // Attribute is mostly used to store important DN requiring processing by Orchestrator. + _('Related DN'), _('DN list filled by main tasks'), + 'fdTasksGranularDN', FALSE + ), + // Below attributes are for tasks of type Mail + new MailAttribute( + _('Email'), + _('Email address to which messages will be sent'), 'fdTasksGranularMail', FALSE), + new MailAttribute( + _('Email'), + _('Email address from which emails will be sent'), 'fdTasksGranularMailFrom', FALSE), + new MailAttribute( + _('Email'), + _('BCC Email address'), 'fdTasksGranularMailBCC', FALSE), + ] + ] + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + parent::__construct($dn, $object, $parent, $mainTab); + } + +} diff --git a/plugins/workflow/tasks/class_tasksMail.inc b/plugins/workflow/tasks/class_tasksMail.inc new file mode 100644 index 0000000000000000000000000000000000000000..8eeba24518c2c32e63b43ee64d689d54069c9469 --- /dev/null +++ b/plugins/workflow/tasks/class_tasksMail.inc @@ -0,0 +1,231 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org) + + Copyright (C) 2018-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +*/ + +class tasksMail extends simplePlugin +{ + protected $displayHeader = TRUE; + + static function plInfo (): array + { + return [ + 'plShortName' => _('Tasks Mail'), + 'plDescription' => _('Tasks Mail Object'), + 'plIcon' => 'geticon.php?context=applications&icon=tasks&size=16', + 'plPriority' => 42, + 'plObjectClass' => ['fdTasksMail'], + 'plFilter' => '(objectClass=fdTasksMail)', + 'plObjectType' => ['tasks'], + 'plConflicts' => ['tasksLifeCycle'], + 'plProvidedAcls' => parent::generatePlProvidedAcls(static::getAttributesInfo()), + 'plForeignKeys' => [ + 'fdTasksMailUsers' => [ + ['user', 'dn', 'fdTasksMailUsers=%oldvalue%', '*'] + ] + ], + ]; + } + + static function getAttributesInfo (): array + { + return [ + // Attributes are grouped by section + 'taskMail' => [ + 'name' => _('Task Mail Object'), + 'attrs' => [ + new SelectAttribute( + _('Mail Template'), _('Mail Template Object Selection'), + 'fdTasksMailObject', FALSE + ), + new HiddenArrayAttribute('fdTasksEmailsFromDN', FALSE, ''), + ] + ], + 'From Component' => [ + 'name' => _('Sender and BCC email address'), + 'attrs' => [ + new MailAttribute( + _('Sender email address'), + _('Email address from which mails will be sent'), 'fdTasksEmailSender', TRUE, 'to.be@chang.ed'), + new MailAttribute( + _('BCC email address'), + _('Email address to put in BCC'), 'fdTasksEmailBCC', FALSE, ''), + ] + ], + 'UserGroupSelection' => [ + 'name' => _('Recipients Users and/or Groups'), + 'attrs' => [ + new UsersGroupsRolesAttribute( + _('Members'), _('Users or groups to assign to this task.'), + 'fdTasksMailUsers', TRUE + ), + ], + ], + 'tasksMailType' => [ + 'name' => _('Type of e-mail address desired'), + 'attrs' => [ + new SelectAttribute( + _('Mail Type - If not found, priority will apply'), _('Mail Type Object Selection'), + 'fdTasksMailType', FALSE + ), + ], + ] + ]; + } + + function __construct ($dn = NULL, $object = NULL, $parent = NULL, $mainTab = FALSE) + { + global $config; + parent::__construct($dn, $object, $parent, $mainTab); + + //Search within LDAP and retrieve all mail objects for current base. + $ldap = $config->get_ldap_link(); + $ldap->cd($config->current['BASE']); + $ldap->search('(&(objectClass=fdMailTemplate))', ['cn']); + $tmpSearch = []; + while ($attrs = $ldap->fetch()) { + $tmpSearch[$attrs['cn'][0]] = $attrs['cn'][0]; + } + asort($tmpSearch); + $this->attributesAccess['fdTasksMailObject']->setChoices(array_keys($tmpSearch), array_values($tmpSearch)); + + $mailAttrTypes = [ + 'mail' => 'mail - [gosaMailAccount primary]', + 'gosaMailAlternateAddress' => 'gosaMailAlternateAddress', + 'gosaMailForwardingAddress' => 'gosaMailForwardingAddress', + 'supannAutreMail' => 'supannAutreMail', + 'supannMailPerso' => 'supannMailPerso', + 'supannMailPrive' => 'supannMailPrive' + ]; + + $this->attributesAccess['fdTasksMailType']->setChoices(array_keys($mailAttrTypes), array_values($mailAttrTypes)); + + } + + /* + * Must return bool to be compliant with the interface + * Allows attributes values based on others to be updated before save. + */ + public function update (): bool + { + parent::update(); + + // get the value of type mail desired and if null set default + $mailAttr = $this->attributesAccess['fdTasksMailType']->getValue(); + if (empty($mailAttr)) { + $mailAttr = 'mail'; + } + + // create a method which return the objectype and attribute names required + $mailObject = $this->getMailObject($mailAttr); + // Get the members or groups selected + $attributeValue = $this->attributesAccess['fdTasksMailUsers']->getValue(); + + // Extract members from potential groups and dyn-groups. + $listOfDN = tasks::extractMembersFromGroups($attributeValue); + // send the objectype and attrs name to the below method + $this->attributesAccess['fdTasksEmailsFromDN']->setValue( + array_values(static::getEmailsFromSelectedDN($mailObject, $mailAttr, $listOfDN)) + ); + + return TRUE; + } + + /* + * Return the objectype searched for by setEmailFromSelectedDN + */ + public function getMailObject (string $mailAttr): string + { + switch ($mailAttr) { + case 'mail' : + case 'gosaMailAlternateAddress' : + case 'gosaMailForwardingAddress': + return 'gosaMailAccount'; + + case 'supannAutreMail' : + case 'supannMailPerso' : + case 'supannMailPrive' : + return 'supannPerson'; + + default : + return 'gosaMailAccount'; + } + } + + /* + * Retrieve registered emails for each DN passed. + */ + public static function getEmailsFromSelectedDN ($mailObject, $mailAttr, $listOfDN): array + { + global $config; + $ldap = $config->get_ldap_link(); + $mailList = []; + + foreach ($listOfDN as $dn) { + // Position ldap to the dn required (limit search). + $ldap->cd($dn); + + // filter and attributes should be equals to the arguments passed to this method + $filter = "(objectClass=$mailObject)"; + $attrs = [$mailAttr]; + + $ldap->search($filter, $attrs); + $info = $ldap->fetch(); + + if (!empty($info[$mailAttr][0])) { + // In case of private supann mail, remove the prefix + $mailList[] = preg_replace('/.+?(?=supann)/', '', $info[$mailAttr][0]); + + // Render the mailing list unique, somewhat mandatory when updating the members lists with dynGroups and members. + $mailList = array_unique($mailList); + // A possible enhancement is to recall itself with another mailObject / attr + } + } + return $mailList; + } + + function save (): array + { + $execTasks = $this->parent->getBaseObject()->fdSubTasksActivation ?? NULL; + + if ($execTasks) { + $this->generateSlaveTasks(); + } + + return parent::save(); + } + + /* + * Generate slave tasks, careful that main task cannot be changed cause subtasks are not updated. + * It would be dangerous to edit subs tasks if some are under processed already. + */ + public function generateSlaveTasks () + { + $listOfDN = $this->attributesAccess['fdTasksEmailsFromDN']->getValue(); + $attributeType = 'fdTasksGranularMail'; + + // Ref is supposed to be the mail object CN in this class + $attrs['ref'] = $this->attributesAccess['fdTasksMailObject']->getValue(); + $attrs['from'] = $this->attributesAccess['fdTasksEmailSender']->getValue(); + $attrs['bcc'] = $this->attributesAccess['fdTasksEmailBCC']->getValue(); + + // Call the method from parent tasks object (first tab) to create sub-tasks. + $this->parent->getBaseObject()->createSlaveTasks($listOfDN, $attributeType, $attrs); + } +} diff --git a/plugins/workflow/tasks/class_tasksManagement.inc b/plugins/workflow/tasks/class_tasksManagement.inc new file mode 100644 index 0000000000000000000000000000000000000000..885a0b3b2d2cd9faf1ccb73418ece7dcbab31f52 --- /dev/null +++ b/plugins/workflow/tasks/class_tasksManagement.inc @@ -0,0 +1,59 @@ +<?php +/* + This code is part of FusionDirectory (http://www.fusiondirectory.org/) + Copyright (C) 2011-2022 FusionDirectory + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +class tasksManagement extends management +{ + + protected $skipCpHandler = TRUE; + + /* + * LinkColumn exists by class column + * TasksColumn is a new class for Tasks based on argonaut column definition + */ + public static $columns = [ + //below fdTasksMailObject must be changed to have a type defined within task creation (new ldap attributes ?hidden) + ['LinkColumn', ['attributes' => 'cn', 'label' => 'Tasks']], + ['TasksColumn', ['attributes' => 'fdTasksMailObject', 'label' => 'Types']], + ['Column', ['attributes' => 'fdTasksCreationDate', 'label' => 'Creation Date']], + ['TasksColumn', ['attributes' => 'fdTasksScheduleDate', 'label' => 'Scheduled']], + ['ActionsColumn', ['label' => 'Actions']], + ]; + + static function plInfo () + { + return [ + 'plShortName' => _('Tasks'), + 'plDescription' => _('Provide a mechanism to create and manage tasks'), + 'plTitle' => _('Tasks'), + 'plIcon' => 'geticon.php?context=applications&icon=tasks&size=48', + 'plSection' => 'conf', + 'plPriority' => 40, + 'plManages' => ['tasks'], + 'plProvidedAcls' => [], + ]; + } + + protected function setUpListing () + { + /* Set baseMode to FALSE */ + $this->listing = new managementListing($this, FALSE); + } + +}