├── logs └── .htaccess ├── views ├── ajax-json.twig ├── option │ ├── element │ │ ├── description.twig │ │ ├── error-msg.twig │ │ ├── radio.twig │ │ ├── label.twig │ │ ├── checkbox.twig │ │ ├── domainsid.twig │ │ ├── textarea.twig │ │ ├── authcode.twig │ │ ├── verification_password.twig │ │ ├── default.twig │ │ ├── select.twig │ │ └── editable_list.twig │ ├── disablingMessage.twig │ ├── lines.twig │ ├── page.twig │ ├── permissionSelection.twig │ ├── header.twig │ ├── line.twig │ ├── body.twig │ └── group.twig ├── block-user.twig ├── blog-options-page.twig ├── user-profile-option.twig ├── blog-profile-relationship.twig └── user-profile-ad-attributes.twig ├── src ├── compat-v2 │ ├── monolog_logger.php │ ├── monolog_handler_nullhandler.php │ ├── monolog_registry.php │ ├── logger.php │ └── stubs.php ├── plug-in │ ├── RequirementException.php │ ├── Authentication │ │ ├── AuthenticationException.php │ │ ├── LogoutException.php │ │ ├── SingleSignOn │ │ │ ├── Variable.php │ │ │ ├── Ui │ │ │ │ └── ShowSingleSignOnLink.php │ │ │ └── Profile │ │ │ │ └── Matcher.php │ │ └── VerificationService.php │ ├── Log │ │ ├── Logger.php │ │ └── LogFacade.php │ ├── User │ │ ├── DuplicateEmailPrevention.php │ │ ├── LocalUserResolver.php │ │ ├── ResolveLocalUser.php │ │ └── Profile │ │ │ └── Ui │ │ │ └── PreventPasswordChange.php │ ├── Ui │ │ ├── Validator │ │ │ └── Rule │ │ │ │ ├── FromEmailAddress.php │ │ │ │ ├── DefaultEmailDomain.php │ │ │ │ ├── BaseDnWarn.php │ │ │ │ ├── SelectValueValid.php │ │ │ │ ├── Port.php │ │ │ │ ├── AdAttributeConflict.php │ │ │ │ ├── AccountSuffix.php │ │ │ │ ├── AdminEmail.php │ │ │ │ ├── WordPressMetakeyConflict.php │ │ │ │ ├── AttributeMappingNull.php │ │ │ │ ├── BaseDn.php │ │ │ │ ├── NoDefaultAttributeName.php │ │ │ │ └── DisallowInvalidWordPressRoles.php │ │ └── Menu │ │ │ └── MenuAdapter.php │ ├── LoginState.php │ ├── Configuration │ │ └── Option.php │ ├── Multisite │ │ ├── Site │ │ │ └── Ui │ │ │ │ └── ExtendSiteList.php │ │ └── Ui │ │ │ └── MultisiteMenu.php │ └── Role │ │ └── Mapping.php └── shared │ ├── Util │ ├── Exception.php │ ├── Validator │ │ ├── Rule │ │ │ ├── Rule.php │ │ │ ├── NotEmptyOrWhitespace.php │ │ │ ├── PositiveNumericOrZero.php │ │ │ ├── RuleAdapter.php │ │ │ ├── HasSuffix.php │ │ │ ├── Numeric.php │ │ │ └── Conditional.php │ │ ├── Result.php │ │ └── Validator.php │ ├── Message │ │ ├── Type.php │ │ └── Message.php │ ├── Logger │ │ ├── LogFacade.php │ │ └── Handlers │ │ │ └── FrontendLogHandler.php │ ├── EscapeUtil.php │ ├── Uninstaller.php │ ├── Util.php │ ├── Assert.php │ ├── Session │ │ └── SessionHandler.php │ └── Encryption.php │ ├── WordPress │ ├── Multisite │ │ ├── Ui.php │ │ ├── Option │ │ │ ├── Encryption.php │ │ │ ├── Type.php │ │ │ ├── Provider.php │ │ │ └── Attribute.php │ │ ├── View │ │ │ └── Page │ │ │ │ ├── Page.php │ │ │ │ └── PageAdapter.php │ │ ├── Ui │ │ │ ├── Actions.php │ │ │ └── BlogConfigurationController.php │ │ ├── Util.php │ │ └── Configuration │ │ │ └── Persistence │ │ │ ├── DefaultProfileRepository.php │ │ │ └── ConfigurationRepository.php │ ├── WordPressErrorException.php │ └── WordPressSiteRepository.php │ ├── AdLdap │ └── AdLdapException.php │ ├── Ldap │ ├── Attributes.php │ └── UserQuery.php │ └── ActiveDirectory │ ├── Context.php │ └── Sid.php ├── .editorconfig ├── js ├── app │ ├── app.module.js │ ├── blog-options │ │ ├── controllers │ │ │ ├── ajax.controller.js │ │ │ ├── sync-action.controller.js │ │ │ ├── blog.controller.js │ │ │ ├── logging.controller.js │ │ │ ├── security.controller.js │ │ │ ├── sso.controller.js │ │ │ └── credential.controller.js │ │ └── services │ │ │ └── persistence.service.js │ ├── profile-options │ │ ├── controllers │ │ │ ├── ajax.controller.js │ │ │ ├── delete.controller.js │ │ │ ├── security.controller.js │ │ │ ├── credential.controller.js │ │ │ ├── sso.controller.js │ │ │ └── logging.controller.js │ │ ├── models │ │ │ └── profile.model.js │ │ └── services │ │ │ └── persistence.service.js │ ├── app.nadi.js │ └── shared │ │ ├── services │ │ ├── browser.service.js │ │ ├── template.service.js │ │ ├── notification.service.js │ │ └── list.service.js │ │ └── utils │ │ └── value.util.js ├── model │ └── profile-configuration-model.js ├── blog-profile-relationship.js ├── blog-options.js ├── profile-options.js └── libraries │ ├── ng-notify.min.js │ └── angular-busy.min.js ├── languages ├── next-active-directory-integration-de_DE.mo └── next-active-directory-integration-de_DE_formal.mo ├── uninstall.php ├── functions.php ├── constants.php ├── composer.json ├── css ├── ng-notify.min.css └── alertify.min.css ├── autoload.php └── index.php /logs/.htaccess: -------------------------------------------------------------------------------- 1 | Deny from all -------------------------------------------------------------------------------- /views/ajax-json.twig: -------------------------------------------------------------------------------- 1 | 2 | {{ data|json_encode()|raw }} -------------------------------------------------------------------------------- /src/compat-v2/monolog_logger.php: -------------------------------------------------------------------------------- 1 | {{ description|raw }} 3 | {% endmacro %} -------------------------------------------------------------------------------- /languages/next-active-directory-integration-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeosIT/active-directory-integration2/HEAD/languages/next-active-directory-integration-de_DE.mo -------------------------------------------------------------------------------- /src/compat-v2/monolog_handler_nullhandler.php: -------------------------------------------------------------------------------- 1 | 3 | 4 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/Util/Exception.php: -------------------------------------------------------------------------------- 1 | 8 | * 9 | * @access 10 | */ 11 | class Exception extends \Exception 12 | { 13 | } -------------------------------------------------------------------------------- /src/compat-v2/monolog_registry.php: -------------------------------------------------------------------------------- 1 | 9 | * @access public 10 | */ 11 | class Ui 12 | { 13 | const VERSION_PAGE_JS = '1.0'; 14 | const VERSION_PROFILE_JS = '1.0'; 15 | const VERSION_CSS = '1.0'; 16 | } -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | removePluginSettings(); -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/Rule.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @access 13 | */ 14 | class AuthenticationException extends Exception 15 | { 16 | 17 | } -------------------------------------------------------------------------------- /src/plug-in/Authentication/LogoutException.php: -------------------------------------------------------------------------------- 1 | 11 | * 12 | * @access 13 | */ 14 | class LogoutException extends Exception 15 | { 16 | 17 | } -------------------------------------------------------------------------------- /js/app/app.nadi.js: -------------------------------------------------------------------------------- 1 | // ADI-634 We had to rename this file since some firewalls block files with names containing strings like 'config' or 'password' 2 | 3 | var $valueHelper = next_ad_int.util.ValueHelper; 4 | var $arrayUtil = next_ad_int.util.ArrayUtil; 5 | 6 | // inject $lazy into services and chain the AJAX response with .then($result) or use filters 7 | app.value("$result", function (response) { 8 | return response.data; 9 | }); -------------------------------------------------------------------------------- /src/shared/Util/Message/Type.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Sebastian Weinert 9 | * @author Danny Meißner 10 | * 11 | * @access 12 | */ 13 | class Type 14 | { 15 | const SUCCESS = 'success'; 16 | const ERROR = 'error'; 17 | const WARNING = 'warning'; 18 | } -------------------------------------------------------------------------------- /js/app/shared/services/browser.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('BrowserService', BrowserService); 3 | 4 | BrowserService.$inject = ['$window']; 5 | 6 | function BrowserService($window) { 7 | return { 8 | /** 9 | * Delegate the call to window.location.reload(). 10 | */ 11 | reload: function () { 12 | $window.location.reload(); 13 | } 14 | }; 15 | } 16 | })(); -------------------------------------------------------------------------------- /views/block-user.twig: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/option/disablingMessage.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, profileId, i18n) %} 2 | 3 | {% set permission = getOptionPermission(profileId, optionName) %} 4 | {% set optionDisabled = getMetadata(optionName, 'DISABLED') %} 5 | {% set optionDisabledMessage = getMetadata(optionName, 'DISABLED_MESSAGE') %} 6 | 7 | {% if optionDisabled %} 8 | 9 | {{ optionDisabledMessage|raw }} 10 | 11 | {% else %} 12 | 13 | {{ i18n.optionIsManagedByProfile }} 14 | 15 | {% endif %} 16 | 17 | {% endmacro %} -------------------------------------------------------------------------------- /src/plug-in/Log/Logger.php: -------------------------------------------------------------------------------- 1 | warning($message, $context); 23 | } 24 | } -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Option/Encryption.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Danny Meißner 10 | * 11 | * @access 12 | */ 13 | class Encryption 14 | { 15 | const NONE = 'none', 16 | STARTTLS = 'starttls', 17 | LDAPS = 'ldaps'; 18 | 19 | 20 | public static function getValues() 21 | { 22 | return array( 23 | self::NONE, 24 | self::STARTTLS, 25 | self::LDAPS, 26 | ); 27 | } 28 | } -------------------------------------------------------------------------------- /views/option/lines.twig: -------------------------------------------------------------------------------- 1 | {# macro for generating#} 2 | {% macro create(optionGroupElements, isProfile, i18n) %} 3 | {# #} 4 | {% import "option/line.twig" as line %} 5 | {% for optionName in optionGroupElements %} 6 | {% set permission = getPermissionForOptionAndBlog(optionName) %} 7 | 8 | {% if isProfile is empty %} 9 | 10 | {% if permission > 0 %} 11 | {{ line.create(optionName, false, isProfile, i18n) }} 12 | {% endif %} 13 | 14 | {% else %} 15 | {{ line.create(optionName, true, isProfile, i18n) }} 16 | {% endif %} 17 | {% endfor %} 18 | {% endmacro %} -------------------------------------------------------------------------------- /src/compat-v2/logger.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | abstract class DuplicateEmailPrevention 16 | { 17 | const PREVENT = 'prevent', 18 | CREATE = 'create', 19 | ALLOW = 'allow'; 20 | 21 | private function __construct() 22 | { 23 | 24 | } 25 | 26 | private function __clone() 27 | { 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /js/app/blog-options/controllers/sync-action.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('SyncActionController', SyncActionController); 3 | 4 | SyncActionController.$inject = ['$scope']; 5 | 6 | function SyncActionController($scope) { 7 | var vm = this; 8 | 9 | vm.isSyncEnabled = $scope.syncEnabled == 1; 10 | vm.domainSidSet = $scope.sid == 1; 11 | vm.isUserSet = $scope.syncUserSet == 1; 12 | vm.isPasswordSet = $scope.syncPassSet == 1; 13 | 14 | $scope.enableSync = vm.isSyncEnabled && 15 | vm.domainSidSet && 16 | vm.isUserSet && 17 | vm.isPasswordSet; 18 | } 19 | 20 | 21 | })(); -------------------------------------------------------------------------------- /views/option/page.twig: -------------------------------------------------------------------------------- 1 | {# create an option page for one blog or for one profile #} 2 | {% macro create(prefix, isProfile, i18n) %} 3 | 4 | {% import "option/header.twig" as header %} 5 | {% import "option/body.twig" as body %} 6 | 7 | {% set grouping = getOptionsGrouping() %} 8 | 9 |
10 | 11 | {% if grouping|length > 1 %} 12 | 13 | {{ header.create(prefix) }} 14 | 15 | {{ body.create(prefix, isProfile, i18n) }} 16 | 17 | {% else %} 18 | 19 |

{{ i18n.noOptionsExists }}

20 | 21 | {% endif %} 22 | 23 |
24 | 25 | 26 | {% endmacro %} -------------------------------------------------------------------------------- /views/option/permissionSelection.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, profileId, permissionSelectId) %} 2 | 3 | {% set isPermissionEnabled = isPermissionEnabled(optionName) %} 4 | {% set name = "permission." ~ optionName %} 5 | {% set itemValue = "{{ item.value }}" %} 6 | {% set itemDescription = "{{ item.description }}" %} 7 | 8 | {% if isPermissionEnabled %} 9 | 12 | {% endif %} 13 | {##} 14 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/View/Page/Page.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Sebastian Weinert 10 | * 11 | * @access public 12 | */ 13 | interface Page 14 | { 15 | /** 16 | * Get the page title. 17 | * 18 | * @return mixed 19 | */ 20 | public function getTitle(); 21 | 22 | /** 23 | * Get the menu slug for the page. 24 | * 25 | * @return mixed 26 | */ 27 | public function getSlug(); 28 | 29 | /** 30 | * Get the slug for post requests. 31 | * 32 | * @return mixed 33 | */ 34 | public function wpAjaxSlug(); 35 | } -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | $paramVal) { 26 | $_POST[$paramKey] = $paramVal; 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Option/Type.php: -------------------------------------------------------------------------------- 1 | 9 | * @access public 10 | */ 11 | class Type 12 | { 13 | const TEXT = 'text'; 14 | const NUMBER = 'number'; 15 | const PASSWORD = 'password'; 16 | const CHECKBOX = 'checkbox'; 17 | const RADIO = 'radio'; 18 | const SELECT = 'select'; 19 | const TEXTAREA = 'textarea'; 20 | const CUSTOM = 'custom'; 21 | const COMBOBOX = 'combobox'; 22 | const AUTHCODE = 'authcode'; 23 | const EDITABLE_LIST = 'editable_list'; 24 | const TABLE = 'table'; 25 | const VERIFICATION_PASSWORD = 'verification_password'; 26 | const DOMAIN_SID = 'domain_sid'; 27 | const LABEL = 'label'; 28 | } -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Ui/Actions.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Sebastian Weinert 10 | * 11 | * @access public 12 | */ 13 | class Actions 14 | { 15 | public const ADI_REQUIREMENTS_ALL_ADMIN_NOTICES = 'all_admin_notices'; 16 | 17 | public const ADI_MENU_ADMIN_MENU = 'admin_menu'; 18 | public const ADI_MENU_NETWORK_ADMIN_MENU = 'network_admin_menu'; 19 | public const ADI_MENU_WP_AJAX_PREFIX = 'wp_ajax_'; 20 | 21 | public static function la() { 22 | die("TEST"); 23 | } 24 | private function __construct() 25 | { 26 | } 27 | 28 | private function __clone() 29 | { 30 | } 31 | } -------------------------------------------------------------------------------- /src/shared/AdLdap/AdLdapException.php: -------------------------------------------------------------------------------- 1 | _detailedMessage = $detailedMessage != null ? $detailedMessage : $message; 17 | $this->_ldapError = $ldapError; 18 | $this->_ldapErrno = $ldapErrno; 19 | } 20 | 21 | public function getLdapError() 22 | { 23 | return $this->_ldapError; 24 | } 25 | 26 | public function getLdapErrno() 27 | { 28 | return $this->_ldapErrno; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/NotEmptyOrWhitespace.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | class NotEmptyOrWhitespace extends RuleAdapter 16 | { 17 | /** 18 | * Validate the given data. 19 | * 20 | * @param string $value 21 | * @param array $data 22 | * 23 | * @return mixed 24 | */ 25 | public function validate($value, $data) 26 | { 27 | $value = trim($value); 28 | 29 | if (empty($value)) { 30 | return $this->getMsg(); 31 | } 32 | 33 | return true; 34 | } 35 | } -------------------------------------------------------------------------------- /src/plug-in/Authentication/SingleSignOn/Variable.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Stefan Fiedler 10 | * 11 | * @access 12 | */ 13 | class Variable 14 | { 15 | const REMOTE_USER = 'REMOTE_USER'; 16 | 17 | const X_REMOTE_USER = 'X-REMOTE-USER'; 18 | // ADI-389 see github issue#29 19 | const HTTP_X_REMOTE_USER = 'HTTP_X_REMOTE_USER'; 20 | 21 | const PHP_AUTH_USER = 'PHP_AUTH_USER'; 22 | 23 | public static function getValues() 24 | { 25 | return array( 26 | self::REMOTE_USER, 27 | self::X_REMOTE_USER, 28 | self::HTTP_X_REMOTE_USER, 29 | self::PHP_AUTH_USER 30 | ); 31 | } 32 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/FromEmailAddress.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access 15 | */ 16 | class FromEmailAddress extends RuleAdapter 17 | { 18 | /** 19 | * Validate the given data. 20 | * 21 | * @param string $value 22 | * @param array $data 23 | * 24 | * @return string 25 | */ 26 | public function validate($value, $data) 27 | { 28 | $conflict = (strpos($value, '@') === false && !empty($value)); 29 | 30 | if ($conflict) { 31 | return $this->getMsg(); 32 | } 33 | 34 | return true; 35 | } 36 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/DefaultEmailDomain.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access 15 | */ 16 | class DefaultEmailDomain extends RuleAdapter 17 | { 18 | /** 19 | * Validate the given data. 20 | * 21 | * @param string $value 22 | * @param array $data 23 | * 24 | * @return mixed 25 | */ 26 | public function validate($value, $data) 27 | { 28 | $conflict = $value != "" && strpos($value, '@') !== false; 29 | 30 | if ($conflict) { 31 | return $this->getMsg(); 32 | } 33 | 34 | return true; 35 | } 36 | } -------------------------------------------------------------------------------- /constants.php: -------------------------------------------------------------------------------- 1 | '); 14 | define('NEXT_ACTIVE_DIRECTORY_INTEGRATION_TABLE_CONVERSION_PATTERN', '[%-5level]|%msg|%ex
'); -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/PositiveNumericOrZero.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | class PositiveNumericOrZero extends Numeric 16 | { 17 | /** 18 | * Validate the given data and check if it is zero or a position number. 19 | * 20 | * @param string $value 21 | * @param array $data 22 | * 23 | * @return bool|mixed 24 | */ 25 | public function validate($value, $data) 26 | { 27 | $condition = parent::validate($value, $data) === true && !$this->isNegative($value); 28 | 29 | if ($condition) { 30 | return true; 31 | } 32 | 33 | return $this->getMsg(); 34 | } 35 | } -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/RuleAdapter.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access public 15 | */ 16 | abstract class RuleAdapter implements Rule 17 | { 18 | /** 19 | * The message that will be returned, if the validation failed. 20 | * 21 | * @var string 22 | */ 23 | private $msg; 24 | 25 | /** 26 | * @param $msg 27 | * @param string $type 28 | */ 29 | public function __construct($msg, $type = Type::ERROR) 30 | { 31 | $this->msg = array($type => $msg); 32 | } 33 | 34 | /** 35 | * @return mixed 36 | */ 37 | public function getMsg() 38 | { 39 | return $this->msg; 40 | } 41 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/BaseDnWarn.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @access 15 | */ 16 | class BaseDnWarn extends RuleAdapter 17 | { 18 | 19 | /** 20 | * Validate the given base DN. 21 | * 22 | * @param string $value 23 | * @param array $data 24 | * 25 | * @return bool|mixed 26 | */ 27 | public function validate($value, $data) 28 | { 29 | $dns = ldap_explode_dn($value, 0); 30 | 31 | // check if given base DN starts with dc= 32 | $occurrences = ArrayUtil::countOccurencesStartsWith($dns, 'dc='); 33 | 34 | if ($occurrences == 1) { 35 | return $this->getMsg(); 36 | } 37 | 38 | return true; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /views/option/header.twig: -------------------------------------------------------------------------------- 1 | {# macro for generating tab headers #} 2 | {% macro create(prefix) %} 3 | 4 | {% set grouping = getOptionsGrouping() %} 5 | 6 | 30 | 31 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Option/Provider.php: -------------------------------------------------------------------------------- 1 | 9 | * @access public 10 | */ 11 | interface Provider 12 | { 13 | /** 14 | * Get the option meta data for an option. 15 | * 16 | * @param string $name 17 | * 18 | * @return array|null 19 | */ 20 | public function get($name); 21 | 22 | /** 23 | * Exists the option with the name $name? 24 | * 25 | * @param string $name 26 | * 27 | * @return bool 28 | */ 29 | public function existOption($name); 30 | 31 | /** 32 | * Get all option elements. 33 | * 34 | * @return array 35 | */ 36 | public function getAll(); 37 | 38 | /** 39 | * Get all option elements that are not transient. 40 | * 41 | * @return mixed 42 | */ 43 | public function getNonTransient(); 44 | } -------------------------------------------------------------------------------- /js/app/shared/services/template.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('TemplateService', TemplateService); 3 | 4 | TemplateService.$inject = ['$compile']; 5 | 6 | function TemplateService($compile) { 7 | return { 8 | renderTemplate: renderTemplate 9 | }; 10 | 11 | /** 12 | * Render the template using the given {@code name} template. 13 | * Assign the given {@code $scope} and the rendered template to our {@code targetSelector}. 14 | * 15 | * @param $scope 16 | * @param targetSelector 17 | * @param name 18 | */ 19 | function renderTemplate($scope, targetSelector, name) { 20 | var html = document.getElementById(name).innerHTML; 21 | var el = angular.element(html); 22 | var compiled = $compile(el); 23 | 24 | jQuery(targetSelector).append(el); 25 | compiled($scope); 26 | } 27 | } 28 | })(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "10up/wp_mock": "1.0.1", 4 | "phpunit/phpunit": "10.5.40 as 9.6.20", 5 | "overtrue/phplint": "^2.3", 6 | "brianhenryie/strauss": "^0.11.1", 7 | "php-mock/php-mock-phpunit": "^2.10" 8 | }, 9 | "require": { 10 | "php": ">=8", 11 | "twig/twig": "3.22.0", 12 | "symfony/polyfill-mbstring": "1.20", 13 | "defuse/php-encryption": "2.0.3", 14 | "monolog/monolog": "^2.8.0" 15 | }, 16 | "scripts": { 17 | "strauss": [ 18 | "vendor/bin/strauss" 19 | ], 20 | "twig-patcher": [ 21 | "php twig-patcher.php" 22 | ], 23 | "post-install-cmd": [ 24 | "@strauss", 25 | "@twig-patcher" 26 | ], 27 | "post-update-cmd": [ 28 | "@strauss", 29 | "@twig-patcher" 30 | ] 31 | }, 32 | "extra": { 33 | "strauss": { 34 | "target_directory": "vendor-repackaged", 35 | "namespace_prefix": "Dreitier\\Nadi\\Vendor", 36 | "classmap_prefix": "Dreitier_Nadi_Vendor_", 37 | "constant_prefix": "DREITIER_NADI_VENDOR_" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/plug-in/Log/LogFacade.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access 11 | */ 12 | abstract class LogFacade 13 | { 14 | 15 | private function __construct() 16 | { 17 | } 18 | 19 | private function __clone() 20 | { 21 | } 22 | 23 | /** 24 | * Iterate through all the given $messages and log them as error. 25 | * 26 | * @param array $messages 27 | */ 28 | public static function error(array $messages) 29 | { 30 | foreach ($messages as $message) { 31 | next_ad_int_logger()->error($message); 32 | } 33 | } 34 | 35 | /** 36 | * Iterate through all the given $messages and log them as debug. 37 | * 38 | * @param array $messages 39 | */ 40 | public static function debug(array $messages) 41 | { 42 | foreach ($messages as $message) { 43 | next_ad_int_logger()->debug($message); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /views/option/element/radio.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, isProfile, inputId) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set elements = getMetadata(optionName, 'ELEMENTS') %} 6 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 7 | 8 |
9 | 10 | {% for elementName, elementValue in elements %} 11 | 12 | {% set elementId = inputId ~ '__' ~ loop.index %} 13 | 14 | 15 | 16 | 17 | 18 | {% endfor %} 19 | 20 | {% import "option/element/description.twig" as descriptionElement %} 21 | {{ descriptionElement.create(description) }} 22 | 23 |
24 | 25 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/Util/Logger/LogFacade.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access 11 | */ 12 | abstract class LogFacade 13 | { 14 | 15 | private function __construct() 16 | { 17 | } 18 | 19 | private function __clone() 20 | { 21 | } 22 | 23 | /** 24 | * Iterate through all the given $messages and log them as error. 25 | * 26 | * @param array $messages 27 | */ 28 | public static function error(array $messages) 29 | { 30 | foreach ($messages as $message) { 31 | next_ad_int_logger()->error($message); 32 | } 33 | } 34 | 35 | /** 36 | * Iterate through all the given $messages and log them as debug. 37 | * 38 | * @param array $messages 39 | */ 40 | public static function debug(array $messages) 41 | { 42 | foreach ($messages as $message) { 43 | next_ad_int_logger()->debug($message); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /views/blog-options-page.twig: -------------------------------------------------------------------------------- 1 | 2 | 6 |
7 |

{{ i18n.title }}

8 | 9 |
10 |
11 |
12 | 13 | {% import "option/page.twig" as page %} 14 | {{ page.create('noprefix', false, i18n) }} 15 |
16 |
17 |
18 | 19 | 20 | 22 | 23 | 24 |
25 |

26 |
-------------------------------------------------------------------------------- /src/shared/Util/EscapeUtil.php: -------------------------------------------------------------------------------- 1 | 10 | * 11 | * @access public 12 | */ 13 | class EscapeUtil 14 | { 15 | const HARMLESS_HTML_TAGS = ''; 16 | 17 | private function __construct() 18 | { 19 | } 20 | 21 | private function __clone() 22 | { 23 | } 24 | 25 | /** 26 | * Escape harmful HTML tags from a string or an array with strings 27 | * 28 | * @param $element array|string 29 | * @return array|string|null 30 | */ 31 | public static function escapeHarmfulHtml($element) 32 | { 33 | if (is_string($element)) { 34 | return strip_tags($element, self::HARMLESS_HTML_TAGS); 35 | } 36 | 37 | if (is_array($element)) { 38 | foreach ($element as $key => $value) { 39 | $element[$key] = self::escapeHarmfulHtml($value); 40 | } 41 | return $element; 42 | } 43 | 44 | return null; 45 | } 46 | } -------------------------------------------------------------------------------- /views/user-profile-option.twig: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 23 | 24 |
{{ i18n.userDisabled }} 5 |
6 | 7 | {{ i18n.userDisabled }} 8 | 9 | 15 | 16 | {% if userDisabled %} 17 |

{{ i18n.informationOnLastDisabling ~ disabledReason }}

18 | {% endif %} 19 | 20 |

{{ i18n.warning }}

21 |
22 |
-------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Option/Attribute.php: -------------------------------------------------------------------------------- 1 | 8 | * @access public 9 | */ 10 | class Attribute 11 | { 12 | const TITLE = 'title'; 13 | const TYPE = 'type'; 14 | const DESCRIPTION = 'description'; 15 | const DETAIL = 'detail'; 16 | const ANGULAR_ATTRIBUTES = 'angular_attributes'; 17 | const ANGULAR_BUTTON_ATTRIBUTES = 'angular_button_attributes'; 18 | const SHOW_PERMISSION = 'show_permission'; 19 | const TRANSIENT = 'transient'; 20 | 21 | const ELEMENTS = 'elements'; 22 | const INLINE = 'inline'; 23 | 24 | const TAB_TITLE = 'tabTitle'; 25 | const GROUP_TITLE = 'groupTitle'; 26 | 27 | const DEFAULT_VALUE = 'defaultValue'; 28 | const PERSIST_DEFAULT_VALUE = 'persistDefaultValue'; 29 | 30 | const DISABLED = 'disabled'; 31 | const DISABLED_MESSAGE = 'disabled_message'; 32 | 33 | const SANITIZER = 'sanitizer'; 34 | 35 | const TYPE_STRUCTURE = 'type_structure'; 36 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/SelectValueValid.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | class SelectValueValid extends RuleAdapter 16 | { 17 | /** 18 | * @var array 19 | */ 20 | private $validValues = array(); 21 | 22 | /** 23 | * @param string $msg 24 | * @param array $validValues 25 | */ 26 | public function __construct($msg, array $validValues) 27 | { 28 | parent::__construct($msg); 29 | 30 | $this->validValues = $validValues; 31 | } 32 | 33 | /** 34 | * Validate the given data. 35 | * 36 | * @param string $value 37 | * @param array $data 38 | * 39 | * @return mixed 40 | */ 41 | public function validate($value, $data) 42 | { 43 | if (!in_array($value, $this->validValues)) { 44 | return $this->getMsg(); 45 | } 46 | 47 | return true; 48 | } 49 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/Port.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Sebastian Weinert 13 | * @author Danny Meißner 14 | * 15 | * @access 16 | */ 17 | class Port extends Numeric 18 | { 19 | 20 | /** 21 | * Validate the given data. 22 | * 23 | * @param string $value 24 | * @param array $data 25 | * 26 | * @return mixed 27 | */ 28 | public function validate($value, $data) 29 | { 30 | if (!is_numeric($value) || !$this->isInPortRange($value)) { 31 | return $this->getMsg(); 32 | } 33 | 34 | return true; 35 | } 36 | 37 | /** 38 | * Check if the given number is inside the port range. 39 | * 40 | * @param $value 41 | * 42 | * @return bool 43 | */ 44 | public function isInPortRange($value) 45 | { 46 | if ($value >= 0 && $value <= 65535) { 47 | return true; 48 | } 49 | 50 | return false; 51 | } 52 | } -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Util.php: -------------------------------------------------------------------------------- 1 | 7 | * @author Sebastian Weinert 8 | * @author Danny Meißner 9 | * 10 | * @access 11 | */ 12 | class Util 13 | { 14 | private function __construct() 15 | { 16 | 17 | } 18 | 19 | private function __clone() 20 | { 21 | 22 | } 23 | 24 | /** 25 | * Check if the user is currently on the network Dashboard. 26 | * 27 | * @return bool 28 | */ 29 | public static function isOnNetworkDashboard() 30 | { 31 | $referer = $_SERVER['HTTP_REFERER'] ?? ''; 32 | 33 | // network admin + ajax requests 34 | // see: https://core.trac.wordpress.org/ticket/22589 35 | if (defined('DOING_AJAX') && DOING_AJAX && is_multisite() 36 | && preg_match('#^' . network_admin_url() . '#i', $referer) 37 | ) { 38 | return true; 39 | } 40 | 41 | // it is a multisite installation, the user is network administrator and the current page is the network dashboard 42 | return (is_multisite() && is_super_admin() && is_network_admin()); 43 | } 44 | } -------------------------------------------------------------------------------- /src/plug-in/Authentication/SingleSignOn/Ui/ShowSingleSignOnLink.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access 11 | */ 12 | class ShowSingleSignOnLink 13 | { 14 | /** 15 | * @var bool 16 | */ 17 | private $isRegistered = false; 18 | 19 | /** 20 | * Register our action to add the SSO link to the login page. 21 | */ 22 | public function register() 23 | { 24 | // don't allow multiple registrations of the same LoginService instance 25 | if ($this->isRegistered) { 26 | return; 27 | } 28 | 29 | add_action('login_form', array($this, 'generateLoginFooter'), 1); 30 | } 31 | 32 | /** 33 | * Render the link to re-authenticate using SSO for the login page. 34 | */ 35 | public function generateLoginFooter() 36 | { 37 | $message = __('Log in using SSO', 'next-active-directory-integration'); 38 | $url = esc_url(add_query_arg('reauth', 'sso')); 39 | echo '

' . $message . '

'; 40 | } 41 | } -------------------------------------------------------------------------------- /js/model/profile-configuration-model.js: -------------------------------------------------------------------------------- 1 | document['next_ad_int'] = document['next_ad_int'] || {}; 2 | document['next_ad_int']['model'] = document['next_ad_int']['model'] || {}; 3 | document['next_ad_int']['model']['configuration'] = document['next_ad_int']['model']['configuration'] || {}; 4 | 5 | document['next_ad_int']['model']['configuration']['SinglePropertyValue'] = (function () { 6 | function SinglePropertyValue() { 7 | this.option_value = ''; 8 | this.option_permission = ''; 9 | } 10 | 11 | return SinglePropertyValue; 12 | })(); 13 | 14 | document['next_ad_int']['model']['configuration']['General'] = (function (SinglePropertyValue) { 15 | function General() { 16 | this.profile_name = new SinglePropertyValue(); 17 | this.profile_is_active = new SinglePropertyValue(); 18 | this.is_active = new SinglePropertyValue(); 19 | this.show_menu_test_authentication = new SinglePropertyValue(); 20 | this.show_menu_sync_to_ad = new SinglePropertyValue(); 21 | this.show_menu_sync_to_wordpress = new SinglePropertyValue(); 22 | } 23 | 24 | return General; 25 | })(document['next_ad_int']['model']['configuration']['SinglePropertyValue']); -------------------------------------------------------------------------------- /js/app/shared/services/notification.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('NotificationService', NotificationService); 3 | 4 | NotificationService.$inject = ['ngNotify']; 5 | 6 | function NotificationService(ngNotify) { 7 | return { 8 | isMessage: isMessage, 9 | showMessage: showMessage 10 | }; 11 | 12 | /** 13 | * Check if the given data contains the isMessage flag. 14 | * 15 | * @param data 16 | * 17 | * @returns {boolean} 18 | */ 19 | function isMessage(data) { 20 | return (typeof data['isMessage'] != 'undefined'); 21 | } 22 | 23 | /** 24 | * Check if the given data is a message. If so, trigger the ngNotify messages 25 | * 26 | * @param data 27 | */ 28 | function showMessage(data) { 29 | // if the given data is no message, do not notify anything 30 | if (!this.isMessage(data)) { 31 | return; 32 | } 33 | 34 | var message = data['message']; 35 | var type = data['type']; 36 | 37 | ngNotify.set(message, type) 38 | } 39 | } 40 | })(); -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/AdAttributeConflict.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Sebastian Weinert 13 | * @author Danny Meißner 14 | * 15 | * @access 16 | */ 17 | class AdAttributeConflict extends RuleAdapter 18 | { 19 | /** 20 | * Validate the given data. 21 | * 22 | * @param string $value 23 | * @param array $data 24 | * 25 | * @return mixed 26 | */ 27 | public function validate($value, $data) 28 | { 29 | $conflict = $this->checkAttributeNamesForConflict($value); 30 | 31 | if ($conflict) { 32 | return $this->getMsg(); 33 | } 34 | 35 | return true; 36 | } 37 | 38 | /** 39 | * Simple delegation to {@see Repository::checkAttributeMapping}. 40 | * 41 | * @param $attributeString 42 | * 43 | * @return bool 44 | */ 45 | protected function checkAttributeNamesForConflict($attributeString) 46 | { 47 | return Repository::checkAttributeNamesForConflict($attributeString); 48 | } 49 | } -------------------------------------------------------------------------------- /views/option/element/label.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 6 | {% set newNetwork = isOnNetworkDashboard() %} 7 | 8 | {% if newNetwork %} 9 | {% if not angularAttributes is empty %} 10 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 11 | {% endif %} 12 | {% else %} 13 | 14 | {% if permission == 2 or permission == 1 %} 15 | {% set disabled = "disabled" %} 16 | {% else %} 17 | {% set disabled = "" %} 18 | {% endif %} 19 | 20 | {% if not angularAttributes is empty %} 21 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 22 | {% endif %} 23 | {% endif %} 24 | 25 |
26 | 27 | 28 | 29 |
30 | 31 |

{% import "option/element/description.twig" as descriptionElement %} 32 | {{ descriptionElement.create(description) }}

33 | 34 | {% endmacro %} -------------------------------------------------------------------------------- /js/app/shared/services/list.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('ListService', ListService); 3 | 4 | ListService.$inject = []; 5 | 6 | function ListService() { 7 | var vm = this; 8 | 9 | vm.removeListItem = function (index, listArray) { 10 | listArray.splice(index, 1); 11 | return listArray; 12 | }; 13 | 14 | vm.addListItem = function (newItem, listArray) { 15 | if (newItem) { 16 | listArray.push(newItem); 17 | return listArray; 18 | } 19 | 20 | return listArray; 21 | }; 22 | 23 | vm.parseListArrayToString = function (listArray) { 24 | var stringBuffer = ""; 25 | var arrayLength = listArray.length; 26 | 27 | for (var i = 0; i < arrayLength; i++) { 28 | var value = listArray[i]; 29 | 30 | if (value != '') { 31 | if (i + 1 < arrayLength) { 32 | stringBuffer += value + ";"; 33 | continue; 34 | } 35 | 36 | stringBuffer += value; 37 | } 38 | } 39 | 40 | return stringBuffer; 41 | }; 42 | 43 | return vm; 44 | } 45 | })(); -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Configuration/Persistence/DefaultProfileRepository.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access public 11 | */ 12 | class DefaultProfileRepository 13 | { 14 | const SUFFIX_DEFAULT = 'default'; 15 | 16 | /** 17 | * Find the default profile from the configuration. 18 | * 19 | * @return int -1 if no profile was found. 20 | */ 21 | public function findProfileId() 22 | { 23 | $name = $this->getProfileOptionName(); 24 | $profileId = get_site_option($name, false); 25 | 26 | return (false === $profileId) ? -1 : (int)$profileId; 27 | } 28 | 29 | /** 30 | * Save the new $profileId as the default profile. 31 | * 32 | * @param $profileId 33 | */ 34 | public function saveProfileId($profileId) 35 | { 36 | $name = $this->getProfileOptionName(); 37 | update_site_option($name, $profileId); 38 | } 39 | 40 | /** 41 | * Return the configuration name for the default profile. 42 | * 43 | * @return string 44 | */ 45 | protected function getProfileOptionName() 46 | { 47 | return NEXT_ACTIVE_DIRECTORY_INTEGRATION_PREFIX . ProfileRepository::PREFIX . self::SUFFIX_DEFAULT; 48 | } 49 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/AccountSuffix.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access 15 | */ 16 | class AccountSuffix extends HasSuffix 17 | { 18 | /** 19 | * Validate the given data. 20 | * 21 | * @param string $value 22 | * @param array $data 23 | * 24 | * @return bool|mixed 25 | */ 26 | public function validate($value, $data) 27 | { 28 | if ($this->isList($value)) { 29 | $emails = explode(';', $value); 30 | 31 | foreach ($emails as $email) { 32 | 33 | if ($this->isInvalid($email)) { 34 | return $this->getMsg(); 35 | } 36 | 37 | continue; 38 | } 39 | 40 | return true; 41 | } 42 | 43 | if ($this->isInvalid($value)) { 44 | return $this->getMsg(); 45 | } 46 | 47 | return true; 48 | } 49 | 50 | /** 51 | * Check if the given value is invalid. 52 | * 53 | * @param $entry 54 | * 55 | * @return bool 56 | */ 57 | private function isInvalid($entry) 58 | { 59 | return ($entry != "" && strpos($entry, '@') === false || $entry != "" && $entry[0] != '@'); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/shared/WordPress/WordPressErrorException.php: -------------------------------------------------------------------------------- 1 | 12 | * @access 13 | */ 14 | class WordPressErrorException extends Exception 15 | { 16 | /** @var \WP_Error */ 17 | private $wordPressError; 18 | 19 | public function __construct(\WP_Error $wordPressError) 20 | { 21 | parent::__construct('', 0, null); 22 | 23 | $this->wordPressError = $wordPressError; 24 | } 25 | 26 | /** 27 | * @return \WP_Error 28 | */ 29 | public function getWordPressError() 30 | { 31 | return $this->wordPressError; 32 | } 33 | 34 | /** 35 | * Throw a new {@see WordPressErrorException} using the given $error. If the given value 36 | * is not an instance of {@see WP_Error}, false will be returned. 37 | * 38 | * @param \WP_Error|mixed $error 39 | * 40 | * @return bool 41 | * 42 | * @throws WordPressErrorException 43 | */ 44 | public static function processWordPressError($error) 45 | { 46 | if (!is_wp_error($error)) { 47 | return false; 48 | } 49 | 50 | LogFacade::error($error->get_error_messages()); 51 | 52 | throw new WordPressErrorException($error); 53 | } 54 | } -------------------------------------------------------------------------------- /src/shared/Util/Logger/Handlers/FrontendLogHandler.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class FrontendLogHandler extends AbstractProcessingHandler 14 | { 15 | 16 | private $log; 17 | 18 | private $enabled = false; 19 | 20 | /** 21 | * @param integer $level The minimum logging level at which this handler will be triggered 22 | * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not 23 | */ 24 | public function __construct($level = Logger::DEBUG, $bubble = true) 25 | { 26 | parent::__construct($level, $bubble); 27 | } 28 | 29 | public function enable() 30 | { 31 | $this->enabled = true; 32 | } 33 | 34 | public function disable() 35 | { 36 | $this->enabled = false; 37 | } 38 | 39 | protected function write(array $record): void 40 | { 41 | if (!$this->enabled) { 42 | return; 43 | } 44 | 45 | $this->log[] = $record; 46 | } 47 | 48 | public function getBufferedLog() 49 | { 50 | $log = $this->log; 51 | 52 | // Clean Log 53 | $this->log = []; 54 | 55 | return $log; 56 | } 57 | } -------------------------------------------------------------------------------- /views/option/element/checkbox.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 6 | {% set newNetwork = isOnNetworkDashboard() %} 7 | 8 | {% if permission == 2 or permission == 1 %} 9 | {% set disabled = "disabled" %} 10 | {% else %} 11 | {% set disabled = "" %} 12 | {% endif %} 13 | 14 | {% if not angularAttributes is empty %} 15 | {% if newNetwork %} 16 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 17 | {% else %} 18 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 19 | {% endif %} 20 | {% endif %} 21 | 22 |
23 | 29 |
30 | 31 | {% endmacro %} -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/AdminEmail.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access 15 | */ 16 | class AdminEmail extends HasSuffix 17 | { 18 | 19 | /** 20 | * Validate the given data. 21 | * 22 | * @param string $value 23 | * @param array $data 24 | * 25 | * @return bool|mixed 26 | */ 27 | public function validate($value, $data) 28 | { 29 | if ($this->isList($value)) { 30 | $emails = explode(';', $value); 31 | 32 | foreach ($emails as $email) { 33 | if ($this->isInvalid($email)) { 34 | return $this->getMsg(); 35 | } 36 | 37 | continue; 38 | } 39 | 40 | return true; 41 | } 42 | 43 | if ($this->isInvalid($value)) { 44 | return $this->getMsg(); 45 | } 46 | 47 | return true; 48 | } 49 | 50 | /** 51 | * Check if the given value is invalid. 52 | * 53 | * @param $email 54 | * 55 | * @return bool 56 | */ 57 | private function isInvalid($email) 58 | { 59 | return ($email != "" && strpos($email, '@') === false) 60 | || ($email != "" && $email[0] == '@') 61 | || ($email != "" && $email[strlen($email) - 1] == '@'); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/HasSuffix.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | class HasSuffix extends RuleAdapter 16 | { 17 | /** 18 | * The suffix to check for. 19 | * 20 | * @var string 21 | */ 22 | private $suffix; 23 | 24 | /** 25 | * Adi_Validation_Rule_SuffixRule constructor. 26 | * 27 | * @param string $msg 28 | * @param string $suffix 29 | */ 30 | public function __construct($msg, $suffix) 31 | { 32 | parent::__construct($msg); 33 | $this->suffix = $suffix; 34 | } 35 | 36 | /** 37 | * Validate the given data. 38 | * 39 | * @param string $value 40 | * @param array $data 41 | * 42 | * @return bool|mixed 43 | */ 44 | public function validate($value, $data) 45 | { 46 | if ($value != "" && strpos($value, $this->suffix) == false) { 47 | return $this->getMsg(); 48 | } 49 | 50 | return true; 51 | } 52 | 53 | /** 54 | * Check if the given value is a frontend list. 55 | * 56 | * @param $value 57 | * 58 | * @return bool 59 | */ 60 | public function isList($value) 61 | { 62 | if (strpos($value, ";") !== false) { 63 | return true; 64 | } 65 | 66 | return false; 67 | } 68 | } -------------------------------------------------------------------------------- /views/option/line.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, network, isProfile, i18n) %} 2 | 3 | {% if network %} 4 | {% set preparedId = 'profile' ~ isProfile ~ '__' ~ optionName %} 5 | {% else %} 6 | {% set preparedId = optionName %} 7 | {% endif %} 8 | 9 | {% set inputId = preparedId ~ '__value' %} 10 | {% set permissionSelectionId = preparedId ~ '__permission' %} 11 | 12 | {% set title = getMetadata(optionName, 'TITLE') %} 13 | {% set type = getMetadata(optionName, 'TYPE') %} 14 | 15 | {# render options input #} 16 | {% if optionName != "additional_user_attributes"%} 17 | 18 | {# write comment #} 19 | {{ title }} 20 | 21 | {# write comment #} 22 | 23 | 24 | {% import "option/element.twig" as element %} 25 | {{ element.create(optionName, isProfile, inputId, permissionSelectionId, network, i18n) }} 26 | 27 | 28 | {% else %} 29 | 30 | {% set permission = getPermissionForOptionAndBlog(optionName) %} 31 | 32 | {% import "option/attributes.twig" as attributes %} 33 | {{ attributes.create(optionName, permission, inputId, permissionSelectionId, network, i18n) }} 34 | 35 | {% endif %} 36 | 37 | {% endmacro %} 38 | -------------------------------------------------------------------------------- /src/shared/Util/Validator/Result.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | */ 13 | class Result 14 | { 15 | /** 16 | * @var array 17 | */ 18 | private $validationResult = array(); 19 | 20 | /** 21 | * @param string $name 22 | * @param string $msg 23 | */ 24 | public function addValidationResult($name, $msg) 25 | { 26 | if (!isset($this->validationResult[$name])) { 27 | $this->validationResult[$name] = array(); 28 | } 29 | 30 | $this->validationResult[$name] = $msg; 31 | } 32 | 33 | /** 34 | * @return bool 35 | */ 36 | public function isValid() 37 | { 38 | return (sizeof($this->validationResult) == 0); 39 | } 40 | 41 | /** 42 | * This method will check the current validationResult object and find the first occurrence of an error. 43 | * If a validation error is found, return true; 44 | * 45 | * @return bool 46 | */ 47 | public function containsErrors() 48 | { 49 | foreach ($this->validationResult as $result) { 50 | if (array_key_exists(Type::ERROR, $result)) { 51 | return true; 52 | } 53 | } 54 | 55 | return false; 56 | } 57 | 58 | /** 59 | * @return array 60 | */ 61 | public function getValidationResult() 62 | { 63 | return $this->validationResult; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /views/option/element/domainsid.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 6 | {% set verificationStatus = '{{ verificationStatus }}' %} 7 | {% set newNetwork = isOnNetworkDashboard() %} 8 | {% set disabled = "" %} 9 | 10 | {% if newNetwork %} 11 | {% if not angularAttributes is empty %} 12 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 13 | {% endif %} 14 | {% else %} 15 | 16 | {% if permission == 2 or permission == 1 %} 17 | {% set disabled = "disabled" %} 18 | {% else %} 19 | {% set disabled = "" %} 20 | {% endif %} 21 | 22 | {% if not angularAttributes is empty %} 23 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 24 | {% endif %} 25 | {% endif %} 26 | 27 |
28 | 29 | 30 | 31 |
32 | 33 |

{% import "option/element/description.twig" as descriptionElement %} 34 | {{ descriptionElement.create(description) }}

35 | 36 | {% endmacro %} -------------------------------------------------------------------------------- /views/option/element/textarea.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId, network) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set type = getMetadata(optionName, 'TYPE') %} 6 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 7 | {% set newNetwork = isOnNetworkDashboard() %} 8 | 9 | {% if permission < 3 %} 10 | {% set disabled = "disabled" %} 11 | {% else %} 12 | {% set disabled = "" %} 13 | {% endif %} 14 | 15 | {% if not angularAttributes is empty %} 16 | {% if newNetwork %} 17 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 18 | {% else %} 19 | {% set angularAttributes = angularAttributes ~ ' && (1))"' %} 20 | {% endif %} 21 | {% else %} 22 | {% set angularAttributes = '' %} 23 | {% endif %} 24 | 25 | 26 | 27 | 28 |

29 | {% import "option/element/error-msg.twig" as errorElement %} 30 | {{ errorElement.create(optionName) }} 31 |

32 | 33 | {% import "option/element/description.twig" as descriptionElement %} 34 | {{ descriptionElement.create(description) }} 35 | 36 | {% endmacro %} -------------------------------------------------------------------------------- /js/app/blog-options/services/persistence.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('PersistService', PersistService); 3 | 4 | PersistService.$inject = ['$http', '$result', 'ngNotify']; 5 | 6 | function PersistService($http, $result, ngNotify) { 7 | var vm = this; 8 | 9 | vm.persistData = function (data) { 10 | 11 | // exclude domain_sid from persist 12 | data.verification_username = ""; 13 | data.verification_password = ""; 14 | delete data.verification_status; 15 | delete data.verification_status_message; 16 | 17 | return $http.post('admin-ajax.php', { 18 | action: 'next_ad_int_blog_options', 19 | security: document.next_ad_int.security, 20 | subAction: 'persistOptionsValues', 21 | data: data 22 | }).then($result); 23 | }; 24 | 25 | vm.persist = function ($scope, _data) { 26 | var data = _data || JSON.parse(angular.toJson($scope.option)); 27 | 28 | vm.persistData(data).then(function (response) { 29 | if (typeof response != 'undefined') { 30 | $scope.messages = response.data; 31 | ngNotify.set('Something went wrong!', 'error') 32 | } else { 33 | $scope.messages = {}; 34 | ngNotify.set('Save successful!', 'success'); 35 | } 36 | }); 37 | }; 38 | 39 | return vm; 40 | } 41 | })(); -------------------------------------------------------------------------------- /src/shared/Util/Uninstaller.php: -------------------------------------------------------------------------------- 1 | options); 16 | } 17 | 18 | $sites = WordPressSiteRepository::getSites(); 19 | $firstOptionTable = $wpdb->base_prefix . 'options'; 20 | $tables = array($firstOptionTable); 21 | 22 | for ($i = 2; $i <= sizeof($sites); $i++) { 23 | $optionTable = $wpdb->base_prefix . $i . '_options'; 24 | array_push($tables, $optionTable); 25 | } 26 | 27 | return $tables; 28 | } 29 | 30 | public function deleteAllEntriesFromTable($table, $keyName) { 31 | global $wpdb; 32 | $prefix =NEXT_ACTIVE_DIRECTORY_INTEGRATION_PREFIX; 33 | 34 | $sql = "DELETE FROM $table WHERE $keyName LIKE '$prefix%';"; 35 | $wpdb->query($sql); 36 | } 37 | 38 | public function removePluginSettings() { 39 | global $wpdb; 40 | 41 | $tables = $this->getAllOptionTables(); 42 | foreach($tables as $table) { 43 | $this->deleteAllEntriesFromTable($table, 'option_name'); 44 | } 45 | 46 | if (is_multisite()) { 47 | $this->deleteAllEntriesFromTable($wpdb->sitemeta, 'meta_key'); 48 | } 49 | 50 | $this->deleteAllEntriesFromTable($wpdb->usermeta, 'meta_key'); 51 | } 52 | } -------------------------------------------------------------------------------- /src/shared/Util/Util.php: -------------------------------------------------------------------------------- 1 | 13 | * @access public 14 | */ 15 | class Util 16 | { 17 | private function __construct() 18 | { 19 | } 20 | 21 | /** 22 | * @var Native 23 | */ 24 | private static $native = null; 25 | 26 | /** 27 | * Gain access to PHP's native functions which could be disabled in certain environments 28 | * 29 | * @param none|null|Native {0} 30 | * if no parameter is provided the current Adi_Util_Internal_Native instance is returned. 31 | * if the parameter is explicitly null, the current instance is resetted and replaced with a fresh instance 32 | * if the parameter is not null it is set as $native object. This must be used inside the test environment 33 | * 34 | * @return Native not null 35 | */ 36 | public static function native() 37 | { 38 | $args = func_get_args(); 39 | $instance = self::$native; 40 | 41 | // (re)set native PHP functions? 42 | if (sizeof($args) > 0) { 43 | $instance = $args[0]; 44 | } 45 | 46 | if ($instance == null) { 47 | // create new instance 48 | self::$native = new Native(); 49 | } else { 50 | // get instance from parameter 51 | self::$native = $instance; 52 | } 53 | 54 | return self::$native; 55 | } 56 | } -------------------------------------------------------------------------------- /src/shared/WordPress/WordPressSiteRepository.php: -------------------------------------------------------------------------------- 1 | 10 | * 11 | * @access public 12 | */ 13 | class WordPressSiteRepository 14 | { 15 | private function __clone() 16 | { 17 | } 18 | 19 | /** 20 | * Delegate to either get_sites (for >= 4.6) or wp_get_sites (for < 4.6). 21 | * 22 | * @return mixed 23 | * @see wp_get_sites, get_sites 24 | * 25 | */ 26 | public static function getSites($param = array()) 27 | { 28 | global $wp_version; 29 | 30 | if (version_compare($wp_version, '4.6', '>=')) { 31 | $sites = get_sites($param); 32 | $oldStyle = array(); 33 | 34 | // convert WP_Site to the old array style 35 | foreach ($sites as $site) { 36 | array_push($oldStyle, $site->to_array()); 37 | } 38 | 39 | return $oldStyle; 40 | } 41 | 42 | return wp_get_sites($param); 43 | } 44 | 45 | /** 46 | * Delegate to either get_site (for >= 4.7) or get_blog_details (for < 4.7). 47 | * 48 | * @param null|int $blogId 49 | * @return WP_Site 50 | * @see get_site, get_blog_details 51 | * @issue ADI-419 52 | */ 53 | public static function getSite($blogId = null) 54 | { 55 | global $wp_version; 56 | 57 | if (version_compare($wp_version, '4.7', '>=')) { 58 | return get_site($blogId); 59 | } 60 | 61 | return get_blog_details($blogId); 62 | } 63 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/WordPressMetakeyConflict.php: -------------------------------------------------------------------------------- 1 | 13 | * @author Sebastian Weinert 14 | * @author Danny Meißner 15 | * 16 | * @access 17 | */ 18 | class WordPressMetakeyConflict extends RuleAdapter 19 | { 20 | /** 21 | * Validate the given data. 22 | * 23 | * @param string $value 24 | * @param array $data 25 | * 26 | * @return mixed 27 | */ 28 | public function validate($value, $data) 29 | { 30 | $attributeMapping = $this->convertAttributeMapping($value); 31 | 32 | $metakeyBuffer = array(); 33 | 34 | foreach ($attributeMapping as $attribute) { 35 | 36 | if (sizeof($metakeyBuffer) <= 0) { 37 | $metakeyBuffer[$attribute["wordpress_attribute"]] = true; 38 | continue; 39 | } 40 | 41 | if (isset($metakeyBuffer[$attribute["wordpress_attribute"]])) { 42 | return $this->getMsg(); 43 | } 44 | 45 | $metakeyBuffer[$attribute["wordpress_attribute"]] = true; 46 | } 47 | 48 | return true; 49 | } 50 | 51 | /** 52 | * Simple delegation to {@see Repository::convertAttributeMapping}. 53 | * 54 | * @param $attributeString 55 | * 56 | * @return array 57 | */ 58 | protected function convertAttributeMapping($attributeString) 59 | { 60 | return Repository::convertAttributeMapping($attributeString); 61 | } 62 | } -------------------------------------------------------------------------------- /views/option/element/authcode.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId, i18n) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 6 | {% set authCode = '{{ authCode }}' %} 7 | {% set newNetwork = isOnNetworkDashboard() %} 8 | 9 | {% if not angularAttributes is empty %} 10 | {% if newNetwork %} 11 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 12 | {% else %} 13 | 14 | {% if permission == 2 or permission == 1 %} 15 | {% set disabled = "disabled" %} 16 | {% else %} 17 | {% set disabled = "" %} 18 | {% endif %} 19 | 20 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 21 | {% endif %} 22 | {% endif %} 23 | 24 | 25 | 26 | 27 |   28 | 29 | 31 | 32 |

33 | {% import "option/element/error-msg.twig" as errorElement %} 34 | {{ errorElement.create(optionName) }} 35 |

36 | 37 |

38 | {% import "option/element/description.twig" as descriptionElement %} 39 | {{ descriptionElement.create(description) }} 40 |

41 | 42 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Configuration/Persistence/ConfigurationRepository.php: -------------------------------------------------------------------------------- 1 | 6 | * @author Danny Meißner 7 | */ 8 | interface ConfigurationRepository 9 | { 10 | /** 11 | * Find the value of the given option 12 | * 13 | * @param int $id 14 | * @param string $optionName 15 | * 16 | * @return mixed 17 | */ 18 | public function findSanitizedValue($id, $optionName); 19 | 20 | /** 21 | * Find the raw value of the given option. 22 | * This method is necessary for the migration of the encrypted passwords. 23 | * 24 | * 25 | * @param $siteId 26 | * @param $optionName 27 | * 28 | * @return mixed 29 | */ 30 | public function findRawValue($siteId, $optionName); 31 | 32 | /** 33 | * Persist the given configuration option 34 | * 35 | * @param int $siteId 36 | * @param string $optionName 37 | * @param mixed $optionValue 38 | */ 39 | public function persistSanitizedValue($siteId, $optionName, $optionValue); 40 | 41 | /** 42 | * Get the option permission for the profile and the option. 43 | * 44 | * @param int $profileId 45 | * @param string $optionName 46 | * 47 | * @return array|bool|null|object|void 48 | */ 49 | public function findSanitizedPermission($profileId, $optionName); 50 | 51 | /** 52 | * @param int $profileId 53 | * @param string $optionName 54 | * @param int $optionPermission between [0,3] 55 | * 56 | * @return bool 57 | */ 58 | public function persistSanitizedPermission($profileId, $optionName, $optionPermission); 59 | } -------------------------------------------------------------------------------- /src/compat-v2/stubs.php: -------------------------------------------------------------------------------- 1 | 10 | * @access public 11 | */ 12 | class LoginState 13 | { 14 | /** 15 | * Indicate that the user has been logged in by NADI 16 | * @var bool 17 | */ 18 | private $authenticated = false; 19 | 20 | /** 21 | * null indicates that no authorization has been done 22 | * @var bool|null 23 | */ 24 | private $authorized = null; 25 | 26 | /** 27 | * Return if user has been authenticated by NADI 28 | * @return bool 29 | */ 30 | public function isAuthenticated() 31 | { 32 | return $this->authenticated; 33 | } 34 | 35 | /** 36 | * Return true if user has been authorized by NADI 37 | * @return bool 38 | */ 39 | public function isAuthorized() 40 | { 41 | return $this->authorized; 42 | } 43 | 44 | /** 45 | * Set the NADI authentication has succeeded 46 | */ 47 | public function setAuthenticationSucceeded() 48 | { 49 | $this->authenticated = true; 50 | } 51 | 52 | /** 53 | * Set the NADI authorization has succeeded 54 | */ 55 | public function setAuthorizationSucceeded() 56 | { 57 | $this->authorized = true; 58 | } 59 | 60 | public function setAuthorizationFailed() 61 | { 62 | $this->authorized = false; 63 | } 64 | 65 | /** 66 | * Return true if authentication is valid and authorization is valid or not done 67 | */ 68 | public function isAuthenticatedAndAuthorized() 69 | { 70 | return $this->isAuthenticated() && ($this->isAuthorized() !== false); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/AttributeMappingNull.php: -------------------------------------------------------------------------------- 1 | 13 | * @author Sebastian Weinert 14 | * @author Danny Meißner 15 | * 16 | * @access 17 | */ 18 | class AttributeMappingNull extends RuleAdapter 19 | { 20 | /** 21 | * Validate the given data. 22 | * 23 | * @param string $value 24 | * @param array $data 25 | * 26 | * @return mixed 27 | */ 28 | public function validate($value, $data) 29 | { 30 | $attributeMapping = $this->convertAttributeMapping($value); 31 | 32 | $isAdAttributeUndefinedOrEmpty = isset($attributeMapping[""]) || isset($attributeMapping["undefined"]); 33 | 34 | if ($isAdAttributeUndefinedOrEmpty) { 35 | return $this->getMsg(); 36 | } 37 | 38 | foreach ($attributeMapping as $attribute) { 39 | $isEmpty = $attribute["type"] === "" 40 | || $attribute["type"] === "undefined" 41 | || $attribute["wordpress_attribute"] === "" 42 | || $attribute["wordpress_attribute"] === "undefined" 43 | || $attribute["wordpress_attribute"] === "next_ad_int_"; 44 | 45 | if ($isEmpty) { 46 | return $this->getMsg(); 47 | } 48 | } 49 | 50 | return true; 51 | } 52 | 53 | /** 54 | * Simple delegation to {@see Repository::convertAttributeMapping}. 55 | * 56 | * @param $attributeString 57 | * 58 | * @return array 59 | */ 60 | protected function convertAttributeMapping($attributeString) 61 | { 62 | return Repository::convertAttributeMapping($attributeString); 63 | } 64 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/BaseDn.php: -------------------------------------------------------------------------------- 1 | 13 | * 14 | * @access 15 | */ 16 | class BaseDn extends RuleAdapter 17 | { 18 | 19 | /** 20 | * Validate the given base DN. 21 | * 22 | * @param string $value 23 | * @param array $data 24 | * 25 | * @return bool|mixed 26 | */ 27 | public function validate($value, $data) 28 | { 29 | 30 | // DME: ADI-563 | Github Issue #49 If baseDN is empty there is no need to continue format validation. We return true in order to allow an empty BaseDN 31 | if ($value == '') { 32 | return true; 33 | } 34 | 35 | // check if baseDN starts with a special character 36 | $re = '/[\W]+/'; 37 | preg_match_all($re, $value[0], $matches); 38 | 39 | if ($matches[0]) { 40 | return $this->getMsg(); 41 | } 42 | 43 | // general format is incorrect 44 | $dns = ldap_explode_dn($value, 0); 45 | 46 | if (!$dns) { 47 | return $this->getMsg(); 48 | } 49 | 50 | // last part of DN must be a domain controller object 51 | $lastObject = end($dns); 52 | 53 | // make lowercase for further comparison 54 | $lastObject = strtolower($lastObject); 55 | 56 | // check if first 3 characters are equal to 'dc=' 57 | if (substr($lastObject, 0, 3) !== "dc=") { 58 | return $this->getMsg(); 59 | } 60 | 61 | // check if at least one domain controller is present 62 | $occurrences = ArrayUtil::countOccurencesStartsWith($dns, 'dc='); 63 | 64 | if ($occurrences == 0) { 65 | return $this->getMsg(); 66 | } 67 | 68 | return true; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/Numeric.php: -------------------------------------------------------------------------------- 1 | 10 | * @author Sebastian Weinert 11 | * @author Danny Meißner 12 | * 13 | * @access 14 | */ 15 | class Numeric extends RuleAdapter 16 | { 17 | /** 18 | * Validate the given data. 19 | * 20 | * @param string $value 21 | * @param array $data 22 | * 23 | * @return mixed 24 | */ 25 | public function validate($value, $data) 26 | { 27 | if (!is_numeric($value)) { 28 | return $this->getMsg(); 29 | } 30 | 31 | return true; 32 | } 33 | 34 | /** 35 | * Check if the given value is a negative number. 36 | * 37 | * @param $value 38 | * 39 | * @return bool 40 | */ 41 | public function isNegative($value) 42 | { 43 | if ($value < 0) { 44 | return true; 45 | } 46 | 47 | return false; 48 | } 49 | 50 | /** 51 | * Check if the given value is a positive number. 52 | * 53 | * @param $value 54 | * 55 | * @return bool 56 | */ 57 | public function isPositive($value) 58 | { 59 | if ($value > 0) { 60 | return true; 61 | } 62 | 63 | return false; 64 | } 65 | 66 | /** 67 | * Check if the given value is a float. 68 | * 69 | * @param $value 70 | * 71 | * @return bool 72 | */ 73 | public function isFloat($value) 74 | { 75 | if (is_float($value)) { 76 | return true; 77 | } 78 | 79 | return false; 80 | } 81 | 82 | /** 83 | * Check if the given value is a zero. 84 | * 85 | * @param $value 86 | * 87 | * @return bool 88 | */ 89 | public function isZero($value) 90 | { 91 | if ($value === 0) { 92 | return true; 93 | } 94 | 95 | return false; 96 | } 97 | } -------------------------------------------------------------------------------- /src/shared/Ldap/Attributes.php: -------------------------------------------------------------------------------- 1 | 8 | * @access public 9 | */ 10 | class Attributes 11 | { 12 | /** 13 | * @var array 14 | */ 15 | private $raw = array(); 16 | 17 | /** 18 | * @var array 19 | */ 20 | private $filtered = array(); 21 | 22 | /** 23 | * Ldap_Attributes constructor. 24 | * @param array $raw 25 | * @param array $filtered 26 | */ 27 | public function __construct($raw = array(), $filtered = array()) 28 | { 29 | $this->raw = $raw; 30 | $this->filtered = $filtered; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | public function getFiltered() 37 | { 38 | return $this->filtered; 39 | } 40 | 41 | /** 42 | * @param array $filtered 43 | */ 44 | public function setFiltered($filtered) 45 | { 46 | $this->filtered = $filtered; 47 | } 48 | 49 | /** 50 | * Return a filtered attribute or $default 51 | * @param string $attributeName 52 | * @param null $default 53 | * @return null 54 | */ 55 | public function getFilteredValue($attributeName, $default = null) { 56 | if (isset($this->filtered)) { 57 | if (isset($this->filtered[$attributeName])) { 58 | return $this->filtered[$attributeName]; 59 | } 60 | } 61 | 62 | return $default; 63 | } 64 | 65 | /** 66 | * @return array 67 | */ 68 | public function getRaw() 69 | { 70 | return $this->raw; 71 | } 72 | 73 | /** 74 | * @param array $raw 75 | */ 76 | public function setRaw($raw) 77 | { 78 | $this->raw = $raw; 79 | } 80 | 81 | /** 82 | * @param string $domainSid 83 | */ 84 | public function setDomainSid($domainSid) { 85 | if (isset($this->filtered)) { 86 | $this->filtered["domainsid"] = $domainSid; 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/NoDefaultAttributeName.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Sebastian Weinert 15 | * @author Danny Meißner 16 | * 17 | * @access 18 | */ 19 | class NoDefaultAttributeName extends RuleAdapter 20 | { 21 | /** 22 | * Validate the given data. 23 | * 24 | * @param string $value 25 | * @param array $data 26 | * 27 | * @return mixed 28 | */ 29 | public function validate($value, $data) 30 | { 31 | $attributeMapping = $this->convertAttributeMapping($value); 32 | $attributeMappingMetaKeys = array_map(function ($mapping) { 33 | return $mapping['wordpress_attribute']; 34 | }, $attributeMapping); 35 | 36 | $intersect = array_intersect($attributeMappingMetaKeys, $this->getForbiddenAttributeNames()); 37 | 38 | if (sizeof($intersect) > 0) { 39 | return $this->getMsg(); 40 | } 41 | 42 | return true; 43 | } 44 | 45 | /** 46 | * Simple delegation to {@see Repository::convertAttributeMapping}. 47 | * 48 | * @param $attributeString 49 | * 50 | * @return array 51 | */ 52 | protected function convertAttributeMapping($attributeString) 53 | { 54 | return Repository::convertAttributeMapping($attributeString); 55 | } 56 | 57 | /** 58 | * Get an array with all default attribute meta keys to prevent that the user overrides any of this. 59 | * 60 | * @return array 61 | */ 62 | protected function getForbiddenAttributeNames() 63 | { 64 | return Repository::getDefaultAttributeMetaKeys(); 65 | } 66 | } -------------------------------------------------------------------------------- /js/blog-profile-relationship.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UI utility class for assigning a profile to a to a site 3 | */ 4 | document['next_ad_int'] = document['next_ad_int'] || {}; 5 | document['next_ad_int']['profile_of_blog'] = {}; 6 | 7 | jQuery(document).ready(function () { 8 | 9 | jQuery('#assignProfile').on('click', function (event) { 10 | event.preventDefault(); 11 | 12 | var form = jQuery(this).closest('form'); 13 | var data = { 14 | profile: form.find('[name="profile"]').val(), 15 | allblogs: form.find('[name="allblogs[]"]:checked').map(function () { 16 | return jQuery(this).val(); 17 | }).get() 18 | }; 19 | 20 | jQuery.post(ajaxurl, { 21 | 'action': 'next_ad_int_blog_profile_relationship', 22 | 'data': data, 23 | 'security': document['next_ad_int']['security'] 24 | }, function (response) { 25 | if (response == 0) { 26 | location.reload(); 27 | } else { 28 | alert(response); 29 | } 30 | }); 31 | }); 32 | 33 | jQuery('#assignDefaultProfile').on('click', function (event) { 34 | event.preventDefault(); 35 | 36 | var form = jQuery(this).closest('form'); 37 | var data = { 38 | 'default-profile': form.find('[name="profile"]').val() 39 | }; 40 | 41 | jQuery.post(ajaxurl, { 42 | 'action': 'next_ad_int_blog_profile_relationship', 43 | 'data': data, 44 | 'security': document['next_ad_int']['security'] 45 | }, function (response) { 46 | if (response == 0) { 47 | location.reload(); 48 | } else { 49 | alert(response); 50 | } 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /js/app/shared/utils/value.util.js: -------------------------------------------------------------------------------- 1 | var next_ad_int = next_ad_int || {}; 2 | next_ad_int.util = next_ad_int.util || {}; 3 | 4 | /** 5 | * @type {{findValue, findPermission}} 6 | */ 7 | next_ad_int.util.ValueHelper = (function (ArrayUtil) { 8 | return { 9 | findValue: findValue, 10 | findPermission: findPermission, 11 | findMessage: findMessage 12 | }; 13 | 14 | /** 15 | * Find the item from the given {@code dataArray} using the given {@code name} and return the 16 | * option_value property. 17 | * 18 | * @param name 19 | * @param dataArray 20 | * @param defaultValue 21 | * 22 | * @returns {*} 23 | */ 24 | function findValue(name, dataArray, defaultValue) { 25 | var item = ArrayUtil.findByKey(name, dataArray); 26 | 27 | if (null == item) { 28 | return defaultValue || null; 29 | } 30 | 31 | return item['option_value']; 32 | } 33 | 34 | /** 35 | * Find the item from the given {@code dataArray} using the given {@code name} and return the 36 | * option_permission property. 37 | * 38 | * @param name 39 | * @param dataArray 40 | * @returns {*} 41 | */ 42 | function findPermission(name, dataArray) { 43 | var item = ArrayUtil.findByKey(name, dataArray); 44 | 45 | if (null == item) { 46 | return null; 47 | } 48 | 49 | return item['option_permission']; 50 | } 51 | 52 | /** 53 | * Find the message from the given {@code dataArray} using the given {@code name} and return it. 54 | * 55 | * @param name 56 | * @param dataArray 57 | * 58 | * @returns {*} 59 | */ 60 | function findMessage(name, dataArray) { 61 | return ArrayUtil.findByKey(name, dataArray); 62 | } 63 | })(next_ad_int.util.ArrayUtil); 64 | -------------------------------------------------------------------------------- /views/option/element/verification_password.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId, i18n) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 6 | {% set verificationStatus = '{{ verificationStatus }}' %} 7 | {% set newNetwork = isOnNetworkDashboard() %} 8 | {% set disabled = "" %} 9 | 10 | {% if newNetwork %} 11 | {% if not angularAttributes is empty %} 12 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 13 | {% endif %} 14 | {% else %} 15 | 16 | {% if permission == 2 or permission == 1 %} 17 | {% set disabled = "disabled" %} 18 | {% else %} 19 | {% set disabled = "" %} 20 | {% endif %} 21 | 22 | {% if not angularAttributes is empty %} 23 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 24 | {% endif %} 25 | {% endif %} 26 | 27 | 29 | 32 | 33 |

34 | 35 | 36 |

37 | 38 | {% import "option/element/description.twig" as descriptionElement %} 39 | {{ descriptionElement.create(description) }} 40 | 41 | {% endmacro %} -------------------------------------------------------------------------------- /views/option/element/default.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId, network) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set type = getMetadata(optionName, 'TYPE') %} 6 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 7 | {% set newNetwork = isOnNetworkDashboard() %} 8 | 9 | {% if permission < 3 %} 10 | {% set disabled = "disabled" %} 11 | {% else %} 12 | {% set disabled = "" %} 13 | {% endif %} 14 | 15 | {% if not angularAttributes is empty %} 16 | {% if newNetwork %} 17 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 18 | {% else %} 19 | {% set angularAttributes = angularAttributes ~ ' && (1))"' %} 20 | {% endif %} 21 | {% else %} 22 | {% set angularAttributes = '' %} 23 | {% endif %} 24 | 25 | 26 | {% if optionName == "base_dn" %} 27 | 28 | {% else %} 29 | 30 | {% endif %} 31 | 32 |

33 | {% import "option/element/error-msg.twig" as errorElement %} 34 | {{ errorElement.create(optionName) }} 35 |

36 | 37 | {% import "option/element/description.twig" as descriptionElement %} 38 | {{ descriptionElement.create(description) }} 39 | 40 | {% endmacro %} -------------------------------------------------------------------------------- /src/plug-in/User/LocalUserResolver.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | class LocalUserResolver 13 | { 14 | /** @var ResolveLocalUser[] */ 15 | private array $resolvers = []; 16 | private Logger $logger; 17 | 18 | public function __construct(Logger $logger) 19 | { 20 | $this->logger = $logger; 21 | } 22 | 23 | /** 24 | * @return ResolveLocalUser[] 25 | */ 26 | public function getResolvers(): array 27 | { 28 | return $this->resolvers; 29 | } 30 | 31 | /** 32 | * Add a new resolver to the list of resolvers 33 | * @param ResolveLocalUser $resolveLocalUserBy 34 | * @return $this 35 | */ 36 | public function add(ResolveLocalUser $resolveLocalUserBy): LocalUserResolver 37 | { 38 | $this->resolvers[] = $resolveLocalUserBy; 39 | return $this; 40 | } 41 | 42 | /** 43 | * Try each of the resolvers of this class and return the first match found. 44 | * @return \WP_User|null 45 | */ 46 | public function resolve(): ?\WP_User 47 | { 48 | $usedResolvers = []; 49 | 50 | foreach ($this->resolvers as $resolver) { 51 | $description = $resolver->getDescription() ?? 'unknown'; 52 | $r = $resolver->resolve(); 53 | 54 | // a local WordPress user had been resolved by a given principal 55 | if ($r) { 56 | $this->logger->debug("User resolver '$description' returned a local WordPress user"); 57 | return $r; 58 | } 59 | 60 | $usedResolvers[] = $description; 61 | } 62 | 63 | $this->logger->debug("A local WordPress user could not be found by any of the following resolvers: '" . implode(",", $usedResolvers) . "'"); 64 | 65 | return null; 66 | } 67 | } -------------------------------------------------------------------------------- /src/shared/Util/Validator/Rule/Conditional.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access 11 | */ 12 | class Conditional extends RuleAdapter 13 | { 14 | /** 15 | * @var Rule[] 16 | */ 17 | private $rules = array(); 18 | 19 | /** 20 | * Conditions to match our data against, before we can run our validation. 21 | * 22 | * @var array 23 | */ 24 | private $propertyCondition; 25 | 26 | /** 27 | * @param Rule[] $rules 28 | * @param array $propertyCondition 29 | */ 30 | public function __construct($rules, array $propertyCondition) 31 | { 32 | parent::__construct(''); 33 | $this->rules = $rules; 34 | $this->propertyCondition = $propertyCondition; 35 | } 36 | 37 | /** 38 | * Validate the given data. 39 | * 40 | * @param string $value 41 | * @param array $data 42 | * 43 | * @return bool|mixed 44 | */ 45 | public function validate($value, $data) 46 | { 47 | if (!$this->areConditionsTrue($data)) { 48 | return true; 49 | } 50 | 51 | foreach ($this->rules as $rule) { 52 | $result = $rule->validate($value, $data); 53 | 54 | if (true !== $result) { 55 | return $result; 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | 62 | /** 63 | * Check the given necessary conditions against our current data set. 64 | * 65 | * @param $data 66 | * 67 | * @return bool 68 | */ 69 | protected function areConditionsTrue($data) 70 | { 71 | foreach ($this->propertyCondition as $key => $value) { 72 | $dataValue = $data[$key]; 73 | 74 | if (isset($dataValue['option_value'])) { 75 | $dataValue = $dataValue['option_value']; 76 | } 77 | 78 | if ($dataValue != $value) { 79 | return false; 80 | } 81 | } 82 | 83 | return true; 84 | } 85 | } -------------------------------------------------------------------------------- /src/plug-in/User/ResolveLocalUser.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | class ResolveLocalUser 11 | { 12 | private ?string $principal; 13 | private mixed $searchMethod; 14 | private ?string $description; 15 | 16 | /** 17 | * @param string|null $principal Principal to search for 18 | * @param callable $searchMethod How to search for the given principal 19 | * @param string|null $description Optional description for debug messages 20 | */ 21 | public function __construct(?string $principal, callable $searchMethod, ?string $description = null) 22 | { 23 | $this->principal = $principal; 24 | $this->searchMethod = $searchMethod; 25 | $this->description = $description; 26 | } 27 | 28 | public function getPrincipal(): ?string 29 | { 30 | return $this->principal; 31 | } 32 | 33 | public function getSearchMethod(): callable 34 | { 35 | return $this->searchMethod; 36 | } 37 | 38 | public function getDescription(): ?string 39 | { 40 | return $this->description; 41 | } 42 | 43 | /** 44 | * Passes the getPrincipal to getSearchMethod and executes the search 45 | * @return \WP_User|null 46 | */ 47 | public function resolve(): ?\WP_User 48 | { 49 | $method = $this->getSearchMethod(); 50 | 51 | $r = $method($this->getPrincipal()); 52 | 53 | if ($r instanceof \WP_User) { 54 | return $r; 55 | } 56 | 57 | return null; 58 | } 59 | 60 | /** 61 | * Create a new ResolveLocalUser instance 62 | * 63 | * @param string|null $principal 64 | * @param callable $searchMethod 65 | * @param string|null $description 66 | * @return ResolveLocalUser 67 | */ 68 | public static function by(?string $principal, callable $searchMethod, ?string $description = null): ResolveLocalUser 69 | { 70 | return new static($principal, $searchMethod, $description); 71 | } 72 | } -------------------------------------------------------------------------------- /views/option/element/select.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId) %} 2 | 3 | {% set value = getOptionValue(optionName, 1) %} 4 | 5 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 6 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 7 | {% set elements = getMetadata(optionName, 'ELEMENTS') %} 8 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 9 | {% set newNetwork = isOnNetworkDashboard() %} 10 | 11 | {% if permission == 2 or permission == 1 %} 12 | {% set disabled = "disabled" %} 13 | {% else %} 14 | {% set disabled = "" %} 15 | {% endif %} 16 | {% if not angularAttributes is empty %} 17 | {% if newNetwork %} 18 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 19 | {% else %} 20 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 21 | {% endif %} 22 | {% endif %} 23 | 41 | 42 |

43 | {% import "option/element/error-msg.twig" as errorElement %} 44 | {{ errorElement.create(optionName) }} 45 |

46 | 47 |

48 | {% import "option/element/description.twig" as descriptionElement %} 49 | {{ descriptionElement.create(description) }} 50 |

51 | 52 | {% endmacro %} -------------------------------------------------------------------------------- /js/app/blog-options/controllers/blog.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('BlogController', BlogController); 3 | 4 | BlogController.$inject = ['$scope', '$timeout', 'DataService', 'PersistService', 'ngNotify']; 5 | 6 | function BlogController($scope, $timeout, DataService, PersistService, ngNotify) { 7 | $scope.runningRequests = 0; 8 | 9 | $scope.save = function () { 10 | var data = DataService.mergeScopeOptions($scope); 11 | 12 | PersistService.persistData(data.options).then(function (response) { 13 | $scope.$broadcast('validation', response); 14 | 15 | return response; 16 | }).then(function (result) { 17 | $scope.$emit('reset-form'); 18 | 19 | if (result.status_success === true) { 20 | ngNotify.set(document['next_ad_int']['saving-success'], 'success'); 21 | 22 | } else { 23 | ngNotify.set(document['next_ad_int']['saving-error'], 'error'); 24 | } 25 | 26 | }); 27 | }; 28 | 29 | /** 30 | * Method used for evaluating if a value is present 31 | * 32 | * @returns {boolean} 33 | */ 34 | $scope.is_input_empty = function(input_value) { 35 | return (input_value == '' || !input_value); 36 | }; 37 | 38 | function activate() { 39 | $scope.runningRequests++; 40 | 41 | DataService.loadInitData().then(function (result) { 42 | $scope.$broadcast('options', result['options']); 43 | $scope.$broadcast('ldapAttributes', result['ldapAttributes']); 44 | $scope.$broadcast('dataTypes', result['dataTypes']); 45 | $scope.$broadcast('wpRoles', result['wpRoles']); 46 | 47 | $scope.runningRequests--; 48 | }); 49 | } 50 | 51 | $timeout(activate); 52 | } 53 | })(); -------------------------------------------------------------------------------- /src/shared/Util/Validator/Validator.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * @author Danny Meißner 13 | * 14 | * @access public 15 | */ 16 | class Validator 17 | { 18 | /** 19 | * An array of all validation rules registered. 20 | * 21 | * @var array 22 | */ 23 | private $validationRules = array(); 24 | 25 | /** 26 | * Validate the given data and return a new {@see Result}. 27 | * 28 | * @param array $data 29 | * 30 | * @return Result 31 | */ 32 | public function validate($data) 33 | { 34 | $result = new Result(); 35 | 36 | foreach ($this->validationRules as $name => $rules) { 37 | if (!isset($data[$name])) { 38 | continue; //TODO Revisit 39 | } 40 | 41 | $value = $data[$name]; 42 | 43 | //TODO Find a better option 44 | if (is_array($value) && isset($value['option_value'])) { 45 | $value = $value['option_value']; 46 | } 47 | 48 | foreach ($rules as $rule) { 49 | /** @var Rule $rule */ 50 | $validationResult = $rule->validate($value, $data); 51 | 52 | if (true !== $validationResult) { 53 | $result->addValidationResult($name, $validationResult); 54 | } 55 | } 56 | } 57 | 58 | return $result; 59 | } 60 | 61 | /** 62 | * Add a new rule to our registered rules. 63 | * 64 | * @param string $name 65 | * @param Rule $rule 66 | */ 67 | public function addRule($name, Rule $rule) 68 | { 69 | if (!isset($this->validationRules[$name])) { 70 | $this->validationRules[$name] = array(); 71 | } 72 | 73 | $this->validationRules[$name][] = $rule; 74 | } 75 | 76 | /** 77 | * Return the current validation rules. 78 | * 79 | * @return array 80 | */ 81 | public function getValidationRules() 82 | { 83 | return $this->validationRules; 84 | } 85 | } -------------------------------------------------------------------------------- /views/option/body.twig: -------------------------------------------------------------------------------- 1 | {# macro for generating tab bodies #} 2 | {% macro create(prefix, isProfile, i18n) %} 3 | 4 |
5 | 6 | {% import "option/group.twig" as group %} 7 | {% set grouping = getOptionsGrouping() %} 8 | {% set isOnNetworkDashboard = isOnNetworkDashboard() %} 9 | 10 | {# Create all tab bodies #} 11 | {% for tabName, optionGroups in grouping %} 12 | {% if isOptionGroupVisible(optionGroups) %} 13 | {# Create a tab body #} 14 | {% set tabId = 'tab_body_' ~ prefix ~ '__' ~ loop.index0 %} 15 | 16 | {% if loop.first %} 17 | {% set style = '' %} 18 | {% else %} 19 | {% set style = 'display: none' %} 20 | {% endif %} 21 | 22 | {% set tabId = 'tab_body_' ~ prefix ~ '__' ~ loop.index0 %} 23 | 24 |
26 | {{ group.create(optionGroups, isProfile, i18n) }} 27 | {% if isOnNetworkDashboard %} 28 |

31 | 33 | {% else %} 34 |

38 | 41 | {% endif %} 42 |
43 | {% endif %} 44 | {% endfor %} 45 |
46 | 47 | 48 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/Util/Message/Message.php: -------------------------------------------------------------------------------- 1 | 9 | * @author Sebastian Weinert 10 | * @author Danny Meißner 11 | * 12 | * @access 13 | */ 14 | class Message 15 | { 16 | /** 17 | * @var string $message 18 | */ 19 | private $message; 20 | /** 21 | * @var string $type 22 | */ 23 | private $type; 24 | /** 25 | * @var array 26 | */ 27 | private $additionalInformation = array(); 28 | 29 | private function __construct($message, $type, $additionalInformation) 30 | { 31 | $this->message = $message; 32 | $this->type = $type; 33 | $this->additionalInformation = $additionalInformation; 34 | } 35 | 36 | private function __clone() 37 | { 38 | } 39 | 40 | /** 41 | * Create a new success message. 42 | * 43 | * @param $message 44 | * @param array $additionalInformation 45 | * 46 | * @return Message 47 | */ 48 | public static function success($message, $additionalInformation = array()) 49 | { 50 | return new self($message, Type::SUCCESS, $additionalInformation); 51 | } 52 | 53 | /** 54 | * Create a new error message. 55 | * 56 | * @param $message 57 | * @param array $additionalInformation 58 | * 59 | * @return Message 60 | */ 61 | public static function error($message, $additionalInformation = array()) 62 | { 63 | return new self($message, Type::ERROR, $additionalInformation); 64 | } 65 | 66 | /** 67 | * Add an additional information to our message. 68 | * 69 | * @param $name 70 | * @param $value 71 | */ 72 | public function addAdditionalInformation($name, $value) 73 | { 74 | $this->additionalInformation[$name] = $value; 75 | } 76 | 77 | /** 78 | * Convert the current message to an array. 79 | * 80 | * @return array 81 | */ 82 | public function toArray() 83 | { 84 | return array( 85 | 'message' => $this->message, 86 | 'type' => $this->type, 87 | 'additionalInformation' => $this->additionalInformation, 88 | 'isMessage' => true, 89 | ); 90 | } 91 | } -------------------------------------------------------------------------------- /views/blog-profile-relationship.twig: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | {% set sites = getSites() %} 8 | {% set profileIDs = findAllProfileIds() %} 9 | {% set defaultProfileID = findDefaultProfileId() %} 10 | 11 |

{{ i18n.title }}

12 | 13 |
14 |
15 | {{ i18n.defaultProfile }} 16 | 17 | 28 | 29 | 30 | 32 |
33 |
34 | 35 |
36 |
37 |
{{ table.search_box(i18n.search, 'site-list') }}
38 |
{{ table.display() }}
39 |
40 |
{{ i18n.changeBlogs }}
41 |
42 | 48 | 49 | 51 |
52 |
53 |
54 |
-------------------------------------------------------------------------------- /js/blog-options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by the on 20.07.2015. 3 | */ 4 | jQuery(document).ready(function () { 5 | 6 | 7 | jQuery('.save-blog-options').on('click', function () { 8 | var options = document['next_ad_int']['option_values']['blog']; 9 | var result = {}; 10 | 11 | jQuery.each(options, function (name, obj) { 12 | result[name] = { 13 | 'option_value': obj.option_value() 14 | }; 15 | }); 16 | 17 | jQuery.post( 18 | ajaxurl, 19 | { 20 | 'action': 'next_ad_int_blog_options', 21 | 'data': result, 22 | 'security': document['next_ad_int']['security'] 23 | }, 24 | function (response) { 25 | if (response == 0) { 26 | var url = window.location.href; 27 | url = url.split('&tab='); 28 | url = url[0] + ('&tab=' + jQuery('.nav-tab-active').attr('tab_number')); 29 | window.location.href = url; 30 | } else { 31 | 32 | var options = document['next_ad_int']['option_values']['blog']; 33 | var optionNames = []; 34 | jQuery.each(options, function (name) { 35 | optionNames.push(name); 36 | }); 37 | 38 | var responseObject = jQuery.parseJSON(response); 39 | 40 | optionNames.forEach(function (element) { 41 | 42 | var errorMsgElement = jQuery("#" + element + "_error_msg"); 43 | 44 | if (responseObject[element] != undefined) { 45 | 46 | if (!errorMsgElement.length) { 47 | jQuery("#" + element + "__value").after('

' + responseObject[element] + "

"); 48 | } 49 | } 50 | 51 | if (errorMsgElement.length && responseObject[element] == undefined) { 52 | errorMsgElement.remove(); 53 | } 54 | }); 55 | } 56 | }); 57 | }); 58 | 59 | 60 | }); -------------------------------------------------------------------------------- /autoload.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/src/plug-in', 34 | "Dreitier\\" => __DIR__ . '/src/shared', 35 | ]; 36 | 37 | // register our own namespaces. 38 | // @see https://stackoverflow.com/a/39774973/2545275 39 | foreach ($mapNamespacesToSourceDirectories as $namespace => $sourceDirectory) { 40 | spl_autoload_register(function ($classname) use ($namespace, $sourceDirectory) { 41 | // Check if the namespace matches the class we are looking for 42 | if (preg_match("#^" . preg_quote($namespace) . "#", $classname)) { 43 | // Remove the namespace from the file path since it's psr4 44 | $classname = str_replace($namespace, "", $classname); 45 | $filename = preg_replace("#\\\\#", "/", $classname) . ".php"; 46 | $fullpath = $sourceDirectory . "/$filename"; 47 | 48 | if (file_exists($fullpath)) { 49 | include_once $fullpath; 50 | } 51 | } 52 | }); 53 | } 54 | 55 | // fallback to grant compatability for premium extensions 56 | require_once __DIR__ . '/src/compat-v2/stubs.php'; 57 | 58 | define("NADI_PACKAGES_LOADED", true); 59 | -------------------------------------------------------------------------------- /src/plug-in/Configuration/Option.php: -------------------------------------------------------------------------------- 1 | uninitializedAttributes = $uninitializedAttributes; 23 | } 24 | 25 | /** 26 | * Create a new Option 27 | * @param array $uninitializedAttributes 28 | * @return Option 29 | */ 30 | public static function make(array $uninitializedAttributes = []): Option 31 | { 32 | return new static($uninitializedAttributes); 33 | } 34 | 35 | public function offsetSet($offset, $value): void 36 | { 37 | throw new \Exception("Option container is immutable"); 38 | } 39 | 40 | public function offsetExists($offset): bool 41 | { 42 | return isset($this->uninitializedAttributes[$offset]); 43 | } 44 | 45 | public function offsetUnset($offset): void 46 | { 47 | throw new \Exception("Option container is immutable"); 48 | } 49 | 50 | public function offsetGet($offset): mixed 51 | { 52 | if (isset($this->initializedAttributes[$offset])) { 53 | return $this->initializedAttributes[$offset]; 54 | } 55 | 56 | if (!isset($this->uninitializedAttributes[$offset])) { 57 | return null; 58 | } 59 | 60 | // If attribute is a function, use it to initialize its value. 61 | // This allows accessing options before the 'init' hook (e.g. in `set_current_user`) 62 | // but prevents textdomain loading issues. 63 | $uninitialized = $this->uninitializedAttributes[$offset]; 64 | $initialized = is_callable($uninitialized) ? $uninitialized() : $uninitialized; 65 | 66 | $this->initializedAttributes[$offset] = $initialized; 67 | 68 | return $initialized; 69 | } 70 | } -------------------------------------------------------------------------------- /src/shared/Util/Assert.php: -------------------------------------------------------------------------------- 1 | 8 | * @access public 9 | */ 10 | class Assert 11 | { 12 | private function __construct() 13 | { 14 | } 15 | 16 | /** 17 | * @param mixed $subject 18 | * @param null|string $msg 19 | * @throws \Exception if $subject is null 20 | */ 21 | public static function notNull($subject, $msg = null) 22 | { 23 | if (null === $subject) { 24 | throw new \Exception(($msg ? $msg : "Given parameter must not be null")); 25 | } 26 | } 27 | 28 | /** 29 | * @param bool $subject 30 | * @param null|string $msg 31 | * @throws \Exception if $subject evaluates to false 32 | */ 33 | public static function condition($subject, $msg = null) 34 | { 35 | if ($subject === false) { 36 | throw new \Exception(($msg ? $msg : "Given condition must be true but is '$subject'.")); 37 | } 38 | } 39 | 40 | /** 41 | * @param mixed $subject 42 | * @param null|string $msg 43 | * @throws \Exception If $subject is not numeric 44 | */ 45 | public static function numeric($subject, $msg = null) 46 | { 47 | if (!is_numeric($subject)) { 48 | throw new \Exception(($msg ? $msg : "Given parameter must be numeric but is '$subject'")); 49 | } 50 | } 51 | 52 | /** 53 | * @param mixed $subject 54 | * @param null|string $msg 55 | * @throws \Exception If $subject is null. If it is a string it length must be > 0 (without trimming). 56 | */ 57 | public static function notEmpty($subject, $msg = null) 58 | { 59 | $defaultMsg = "Given parameter must not be empty"; 60 | self::notNull($subject, $msg); 61 | 62 | if (is_string($subject) && (strlen($subject) == 0)) { 63 | throw new \Exception(($msg ? $msg : $defaultMsg)); 64 | } 65 | } 66 | 67 | /** 68 | * @param mixed $subject 69 | * @param null|string $msg 70 | * @throws \Exception If $subject is not numeric or is less than zero. 71 | */ 72 | public static function validId($subject, $msg = null) 73 | { 74 | self::numeric($subject, "Given parameter is not a valid numeric ID"); 75 | 76 | if ($subject <= 0) { 77 | throw new \Exception(($msg ? $msg : "Given parameter must be greater than 0 but is '$subject'")); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /js/profile-options.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by the on 20.07.2015. 3 | */ 4 | jQuery(document).ready(function () { 5 | function showOptionsOfSelectedProfile() { 6 | var selectedProfileId = jQuery('#selectedProfile').val(); 7 | var profileOptionsOverview = '#profileOptionsOverview__' + selectedProfileId; 8 | 9 | jQuery('.profileOptionsOverview').hide(); 10 | jQuery(profileOptionsOverview).show(); 11 | } 12 | 13 | jQuery("#selectedProfile").change(showOptionsOfSelectedProfile); 14 | showOptionsOfSelectedProfile(); 15 | 16 | //change the profile drop down menu to the last selected value 17 | if (window.location.href.indexOf('&profile=') > -1) { 18 | var profileNumber = window.location.href.split('&profile=')[1].split('&')[0]; 19 | jQuery("#selectedProfile").val(parseInt(profileNumber)); 20 | } 21 | 22 | jQuery('.save-blog-options').click(function () { 23 | var selectedProfileId = jQuery('#selectedProfile').val(); 24 | var options = document['next_ad_int']['option_values'][selectedProfileId]; 25 | var result = {}; 26 | 27 | jQuery.each(options, function (name, obj) { 28 | result[name] = { 29 | 'option_value': obj['option_value'](), 30 | 'option_permission': obj['option_permission']() 31 | }; 32 | }); 33 | 34 | jQuery.post( 35 | ajaxurl, 36 | { 37 | 'action': 'next_ad_int_profile_options', 38 | 'data': { 39 | 'options': result, 40 | 'profile': selectedProfileId 41 | }, 42 | 'security': document['next_ad_int']['security'] 43 | }, 44 | function (response) { 45 | if (response == 0) { 46 | var url = window.location.href; 47 | url = url.split('&tab=')[0]; 48 | url = url.split('&profile=')[0]; 49 | url = url + '&tab=' + jQuery('.nav-tab-active[profile_number=' + selectedProfileId + ']').attr('tab_number') + '&profile=' + selectedProfileId; 50 | 51 | window.location.href = url; 52 | } else { 53 | alert(response); 54 | } 55 | } 56 | ); 57 | }); 58 | }) 59 | ; -------------------------------------------------------------------------------- /views/user-profile-ad-attributes.twig: -------------------------------------------------------------------------------- 1 | {% if renderData.attributes|length > 0 %} 2 |

{{ i18n.additionalInformation }}

3 | {% endif %} 4 | 5 | 6 | 7 | {% for attribute in renderData.attributes %} 8 | 9 | 10 | 11 | {% if attribute.noAttribute %} 12 | 13 | 14 | 15 | {% else %} 16 | 17 | 20 | 21 | 33 | 34 | {% endif %} 35 | 36 | 37 | 38 | {% endfor %} 39 | 40 | {% if renderData.adi_is_editable %} 41 | {% if renderData.require_password %} 42 | 43 | 47 | 54 | 55 | {% endif %} 56 | {% else %} 57 | 58 | 61 | 62 | {% endif %} 63 | 64 |
{{ attribute.description }} 18 | 19 | 22 | {% if attribute.outputType == 'textarea' %} 23 | 25 | {% elseif attribute.outputType == 'text' %} 26 | 28 | {% else %} 29 | {{ attribute.value|nl2br }} 30 | {% endif %} 31 | 32 |
44 | 46 | 48 | 50 |

51 | {{ i18n.youMustEnterPassword }} 52 |

53 |

59 | {{ i18n.canNotBeEdited }} ~ {{ renderData.adi_synchronization_error_message }}

60 |
-------------------------------------------------------------------------------- /src/plug-in/User/Profile/Ui/PreventPasswordChange.php: -------------------------------------------------------------------------------- 1 | 15 | * @author Christopher Klein 16 | * @access public 17 | */ 18 | class PreventPasswordChange 19 | { 20 | /** @var $multisiteConfigurationService Service */ 21 | private $multisiteConfigurationService; 22 | 23 | /** @var $userManager Manager */ 24 | private $userManager; 25 | 26 | /** 27 | * @param Service $multisiteConfigurationService 28 | * @param Manager $userManager 29 | */ 30 | public function __construct(Service $multisiteConfigurationService, 31 | Manager $userManager) 32 | { 33 | $this->multisiteConfigurationService = $multisiteConfigurationService; 34 | $this->userManager = $userManager; 35 | } 36 | 37 | /** 38 | * Disable password fields for a user. 39 | */ 40 | public function register() 41 | { 42 | // the callback functions have to decide whether to execute thm or not b/c they require the current user context 43 | 44 | // Is local password change disallowed? 45 | // disable password fields 46 | add_filter('show_password_fields', array($this, 'showPasswordFields'), 10, 2); 47 | } 48 | 49 | /** 50 | * Return the value of th setting "Allow local password changes" 51 | * @return bool 52 | */ 53 | public function isPasswordChangeEnabled() 54 | { 55 | return $this->multisiteConfigurationService->getOptionValue(Options::ENABLE_PASSWORD_CHANGE); 56 | } 57 | 58 | /** 59 | * WordPress callback for showing/hiding the password fields 60 | * 61 | * @param bool $show setting from prior called plug-ins 62 | * @param WP_User $wpUser 63 | * @return mixed bool 64 | */ 65 | public function showPasswordFields($show, $wpUser) 66 | { 67 | // if user is no AD member or a local user (admin etc.), the parent value has priority 68 | if (!$this->userManager->hasActiveDirectoryAccount($wpUser)) { 69 | return $show; 70 | } 71 | 72 | // user is AD member so decide by ADI setting 73 | return $this->isPasswordChangeEnabled(); 74 | } 75 | } -------------------------------------------------------------------------------- /js/app/profile-options/services/persistence.service.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.service('PersistService', PersistService); 3 | 4 | PersistService.$inject = ['$http', '$result', 'NotificationService']; 5 | 6 | function PersistService($http, $result, NotificationService) { 7 | var vm = this; 8 | 9 | vm.assignProfile = function (blogs, profileId) { 10 | var data = { 11 | profile: profileId, 12 | allblogs: jQuery.map(blogs, function (item) { 13 | return item['blog_id']; 14 | }) 15 | }; 16 | 17 | return $http.post('../admin-ajax.php', { 18 | action: 'next_ad_int_blog_profile_relationship', 19 | data: data, 20 | security: document['next_ad_int']['blog-rel-security'] 21 | }).then(function () { 22 | 23 | }); 24 | }; 25 | 26 | vm.removeProfile = function (id) { 27 | return $http.post('../admin-ajax.php', { 28 | action: 'next_ad_int_profile_options', 29 | security: document.next_ad_int.security, 30 | subAction: 'removeProfile', 31 | id: id 32 | }).then($result); 33 | }; 34 | 35 | vm.persistData = function (data) { 36 | return $http.post('../admin-ajax.php', { 37 | action: 'next_ad_int_profile_options', 38 | security: document.next_ad_int.security, 39 | subAction: 'persistProfileOptionsValues', 40 | data: data 41 | }).then($result); 42 | }; 43 | 44 | vm.persist = function ($scope, _data, profileId) { 45 | var data = _data || JSON.parse(angular.toJson($scope.option)); 46 | var dataPermission = JSON.parse(angular.toJson($scope.permission)); 47 | 48 | var dataBuffer = {"options": {}}; 49 | 50 | for (var option in data) { 51 | dataBuffer["options"][option] = { 52 | "option_value": data[option], 53 | "option_permission": dataPermission[option] 54 | }; 55 | } 56 | 57 | dataBuffer["profile"] = profileId; 58 | 59 | return vm.persistData(dataBuffer).then($result).then(function (response) { 60 | NotificationService.showMessage(response); 61 | }); 62 | }; 63 | 64 | return vm; 65 | } 66 | })(); -------------------------------------------------------------------------------- /views/option/group.twig: -------------------------------------------------------------------------------- 1 | {# macro for generating option groups for a single tab body#} 2 | {% macro create(optionGroups, isProfile, i18n) %} 3 | 4 | 14 | 15 | {% import "option/lines.twig" as lines %} 16 | {% set newNetwork = isOnNetworkDashboard() %} 17 | 18 | {# Add all option groups of the current tab #} 19 | {% for optionGroupName, optionGroup in optionGroups %} 20 | 21 | {# Display the optionGroupName if it is not empty#} 22 | {% if optionGroupName and (optionGroupName != 'angular_controller' and optionGroupName != 'multisite_only') %} 23 | 24 |

{{ optionGroupName }}

25 | 26 | {% endif %} 27 | 28 | {# Add the description or the array of descriptions to this option group #} 29 | {% if optionGroup.description is defined %} 30 | {{ optionGroup.description|join('
')|raw }} 31 | {% if optionGroupName is defined and newNetwork%} 32 | 33 | 34 | 35 | 36 | 37 |
 {{ i18n.managePermissions }}
38 | {% endif %} 39 | {% endif %} 40 | 41 | {# Do not add zero options#} 42 | {% if optionGroup.options is defined %} 43 | {# Add all option of this option group#} 44 | 45 | {{ lines.create(optionGroup.options, isProfile, i18n) }} 46 |
47 | {% endif %} 48 | 49 | {% endfor %} 50 | 51 | {% endmacro %} -------------------------------------------------------------------------------- /views/option/element/editable_list.twig: -------------------------------------------------------------------------------- 1 | {% macro create(optionName, permission, inputId) %} 2 | 3 | {% set inline = getMetadata(optionName, 'INLINE')|join(' ') %} 4 | {% set description = getMetadata(optionName, 'DESCRIPTION') %} 5 | {% set elements = getMetadata(optionName, 'ELEMENTS') %} 6 | {% set angularAttributes = getMetadata(optionName, 'ANGULAR_ATTRIBUTES') %} 7 | {% set angularButtonAttributes = getMetadata(optionName, 'ANGULAR_BUTTON_ATTRIBUTES') %} 8 | {% set typeStructure = getMetadata(optionName, 'TYPE_STRUCTURE') %} 9 | {% set newNetwork = isOnNetworkDashboard() %} 10 | 11 | 12 | {% if permission == 2 or permission == 1 %} 13 | {% set disabled = "disabled" %} 14 | {% else %} 15 | {% set disabled = "" %} 16 | {% endif %} 17 | 18 | {% if not angularAttributes is empty %} 19 | {% if newNetwork %} 20 | {% set angularAttributes = angularAttributes ~ ' && (0))"' %} 21 | {% else %} 22 | {% set angularAttributes = angularAttributes ~ '&& (1))"' %} 23 | {% endif %} 24 | {% endif %} 25 | 26 | 27 | 31 |
32 | 33 |

34 | {% import "option/element/error-msg.twig" as errorElement %} 35 | {{ errorElement.create(optionName) }} 36 |

37 | 38 |
    39 |
  • 40 |
    41 | 42 | 46 |
    47 |
  • 48 |
49 | 50 | {% import "option/element/description.twig" as descriptionElement %} 51 | {{ descriptionElement.create(description) }} 52 | 53 | {% endmacro %} -------------------------------------------------------------------------------- /src/shared/ActiveDirectory/Context.php: -------------------------------------------------------------------------------- 1 | 8 | * @access public 9 | * @since 2.2.0 10 | */ 11 | class Context 12 | { 13 | /** 14 | * List of domain SIDs 15 | * 16 | * @var array 17 | */ 18 | private $domainSids; 19 | 20 | /** 21 | * @param $domainSids 22 | * @throws \Exception 23 | */ 24 | public function __construct(array $domainSids) 25 | { 26 | if (!is_array($domainSids) || (sizeof($domainSids) == 0)) { 27 | throw new \Exception("Atleast one domain SID must be configured"); 28 | } 29 | 30 | $this->domainSids = array_map('strtoupper', $domainSids); 31 | } 32 | 33 | /** 34 | * Get the first defined domain SID, in an AD forest, this is the SID of the domain which NADI is connected to. 35 | * @return mixed 36 | */ 37 | public function getPrimaryDomainSid() 38 | { 39 | return $this->domainSids[0]; 40 | } 41 | 42 | /** 43 | * Retrieve all SIDs for this context. 44 | * @return array of strings 45 | */ 46 | public function getForestSids() 47 | { 48 | return $this->domainSids; 49 | } 50 | 51 | /** 52 | * Return true if the given SID is part of one the SIDs defined for the context 53 | * 54 | * @param ?Sid $objectSid 55 | * @param false $primaryDomainOnly 56 | * @return bool 57 | */ 58 | public function isMember(?Sid $objectSid, $primaryDomainOnly = false) 59 | { 60 | if (!$objectSid) { 61 | return false; 62 | } 63 | 64 | $useSid = $objectSid->getDomainPartAsSid(); 65 | 66 | if (!$useSid) { 67 | return false; 68 | } 69 | 70 | $sidAsString = $useSid->getFormatted(); 71 | $checkSids = $primaryDomainOnly ? [$this->domainSids[0]] : $this->domainSids; 72 | 73 | $r = in_array($sidAsString, $checkSids); 74 | 75 | return apply_filters(NEXT_ACTIVE_DIRECTORY_INTEGRATION_PREFIX . 'object_has_ad_context_membership', $r, $objectSid, $primaryDomainOnly, $this); 76 | } 77 | 78 | /** 79 | * Check the membership and throws an exception if the SID is not a member 80 | * 81 | * @param Sid $objectSid 82 | * @return bool 83 | * @throws \Exception 84 | */ 85 | public function checkMembership(Sid $objectSid) { 86 | if (!$this->isMember($objectSid)) { 87 | throw new \Exception("SID '" . $objectSid . "' is not member of any of the following domain SIDs: " . $this); 88 | } 89 | 90 | return true; 91 | } 92 | 93 | public function __toString() { 94 | return "Context={domainSids={" . implode(",", $this->domainSids) . "}}" ; 95 | } 96 | } -------------------------------------------------------------------------------- /css/alertify.min.css: -------------------------------------------------------------------------------- 1 | .alertify-logs>*{padding:12px 24px;color:#fff;box-shadow:0 2px 5px 0 rgba(0,0,0,.2);border-radius:1px}.alertify-logs>*,.alertify-logs>.default{background:rgba(0,0,0,.8)}.alertify-logs>.error{background:rgba(244,67,54,.8)}.alertify-logs>.success{background:rgba(76,175,80,.9)}.alertify{position:fixed;background-color:rgba(0,0,0,.3);left:0;right:0;top:0;bottom:0;width:100%;height:100%;z-index:99999}.alertify.hide{opacity:0;pointer-events:none}.alertify,.alertify.hide,.alertify.show{box-sizing:border-box;-webkit-transition:all .33s cubic-bezier(.25,.8,.25,1);transition:all .33s cubic-bezier(.25,.8,.25,1)}.alertify,.alertify *{box-sizing:border-box}.alertify .dialog{padding:12px}.alertify .alert,.alertify .dialog{width:100%;margin:0 auto;position:relative;top:50%;-webkit-transform:translateY(-50%);-ms-transform:translateY(-50%);transform:translateY(-50%)}.alertify .alert>*,.alertify .dialog>*{width:400px;max-width:95%;margin:0 auto;text-align:center;padding:12px;background:#fff;box-shadow:0 2px 4px -1px rgba(0,0,0,.14),0 4px 5px 0 rgba(0,0,0,.098),0 1px 10px 0 rgba(0,0,0,.084)}.alertify .alert .msg,.alertify .dialog .msg{padding:12px;margin:0;text-align:left}.alertify .alert input,.alertify .dialog input{margin-bottom:15px;width:100%;font-size:100%;padding:12px}.alertify .alert input:focus,.alertify .dialog input:focus{outline-offset:-2px}.alertify .alert nav,.alertify .dialog nav{text-align:right}.alertify .alert nav button,.alertify .dialog nav button{background:0 0;box-sizing:border-box;color:rgba(0,0,0,.87);position:relative;outline:0;border:0;display:inline-block;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;padding:0 6px;margin:6px 8px;line-height:36px;min-height:36px;white-space:nowrap;min-width:88px;text-align:center;text-transform:uppercase;font-size:14px;text-decoration:none;cursor:pointer;border-radius:2px}.alertify .alert nav button:active,.alertify .alert nav button:hover,.alertify .dialog nav button:active,.alertify .dialog nav button:hover{background-color:rgba(0,0,0,.05)}.alertify .alert nav button:focus,.alertify .dialog nav button:focus{border-style:dashed}.alertify-logs{position:fixed;z-index:100;bottom:16px;left:16px}.alertify-logs>*{box-sizing:border-box;-webkit-transition:all .3s cubic-bezier(.25,.8,.25,1);transition:all .3s cubic-bezier(.25,.8,.25,1);margin-top:10px;position:relative;float:left;clear:both;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.alertify-logs>.show{left:0;opacity:1}.alertify-logs>*,.alertify-logs>.hide{left:-110%;opacity:0} -------------------------------------------------------------------------------- /js/app/blog-options/controllers/logging.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | app.controller('LoggingController', LoggingController); 4 | 5 | LoggingController.$inject = ['$scope', '$http', 'DataService', 'PersistService', 'ngNotify']; 6 | 7 | function LoggingController($scope, $http, DataService, PersistService, ngNotify) { 8 | var vm = this; 9 | 10 | $scope.isSaveDisabled = false; 11 | 12 | $scope.permissionOptions = DataService.getPermissionOptions(); 13 | 14 | 15 | $scope.$on('options', function (event, data) { 16 | $scope.option = { 17 | logger_enable_logging: $valueHelper.findValue("logger_enable_logging", data), 18 | logger_custom_path: $valueHelper.findValue("logger_custom_path", data), 19 | }; 20 | 21 | if ($valueHelper.findValue("domain_sid", data) == '') { 22 | $scope.isSaveDisabled = true; 23 | } 24 | 25 | $scope.permission = { 26 | logger_enable_logging: $valueHelper.findPermission("logger_enable_logging", data), 27 | logger_custom_path: $valueHelper.findPermission("logger_custom_path", data), 28 | }; 29 | }); 30 | 31 | $scope.$on('validation', function (event, data) { 32 | $scope.messages = { 33 | logger_enable_logging: $valueHelper.findMessage("logger_enable_logging", data), 34 | logger_custom_path: $valueHelper.findMessage("logger_custom_path", data), 35 | }; 36 | }); 37 | 38 | $scope.$on('verification', function (event, data) { 39 | $scope.isSaveDisabled = false; 40 | }); 41 | 42 | $scope.containsErrors = function () { 43 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 44 | }; 45 | 46 | $scope.activateLogging = function () { 47 | console.log("Save Logging information"); 48 | 49 | var data = { 50 | options : { 51 | logger_enable_logging : $scope.option.logger_enable_logging, 52 | logger_custom_path : $scope.option.logger_custom_path 53 | } 54 | }; 55 | 56 | PersistService.persistData(data.options).then(function (response) { 57 | 58 | if (response.status_success === true) { 59 | ngNotify.set(document['next_ad_int']['saving-success'], 'success'); 60 | 61 | } else { 62 | ngNotify.set(document['next_ad_int']['saving-error'], 'error'); 63 | } 64 | }); 65 | } 66 | } 67 | })(); -------------------------------------------------------------------------------- /js/app/blog-options/controllers/security.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('SecurityController', SecurityController); 3 | 4 | SecurityController.$inject = ['$scope', 'ListService', 'DataService']; 5 | 6 | function SecurityController($scope, ListService, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.permissionOptions = DataService.getPermissionOptions(); 12 | 13 | $scope.$on('options', function (event, data) { 14 | $scope.option = { 15 | enable_smartcard_user_login: $valueHelper.findValue("enable_smartcard_user_login", data), 16 | custom_login_page_enabled: $valueHelper.findValue("custom_login_page_enabled", data), 17 | custom_login_page_uri: $valueHelper.findValue("custom_login_page_uri", data), 18 | allow_xmlrpc_login: $valueHelper.findValue("allow_xmlrpc_login", data), 19 | }; 20 | 21 | if ($valueHelper.findValue("domain_sid", data) == '') { 22 | $scope.isSaveDisabled = true; 23 | } 24 | 25 | $scope.permission = { 26 | enable_smartcard_user_login: $valueHelper.findPermission("enable_smartcard_user_login", data), 27 | custom_login_page_enabled: $valueHelper.findPermission("custom_login_page_enabled", data), 28 | custom_login_page_uri: $valueHelper.findPermission("custom_login_page_uri", data), 29 | allow_xmlrpc_login: $valueHelper.findPermission("allow_xmlrpc_login", data), 30 | }; 31 | }); 32 | 33 | $scope.$on('validation', function (event, data) { 34 | $scope.messages = { 35 | enable_smartcard_user_login: $valueHelper.findMessage("enable_smartcard_user_login", data), 36 | custom_login_page_enabled: $valueHelper.findMessage("custom_login_page_enabled", data), 37 | custom_login_page_uri: $valueHelper.findMessage("custom_login_page_uri", data), 38 | allow_xmlrpc_login: $valueHelper.findMessage("allow_xmlrpc_login", data) 39 | }; 40 | }); 41 | 42 | $scope.$on('verification', function (event, data) { 43 | $scope.isSaveDisabled = false; 44 | }); 45 | 46 | $scope.getPreparedOptions = function () { 47 | var data = DataService.cleanOptions($scope.option); 48 | return data; 49 | }; 50 | 51 | $scope.containsErrors = function () { 52 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 53 | }; 54 | 55 | $scope.save = function() { 56 | $scope.$parent.save(); 57 | }; 58 | } 59 | })(); -------------------------------------------------------------------------------- /js/app/profile-options/controllers/delete.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('DeleteController', DeleteController); 3 | 4 | DeleteController.$inject = ['$rootScope', '$scope', 'PersistService', 'BrowserService']; 5 | 6 | function DeleteController($rootScope, $scope, PersistService, BrowserService) { 7 | $scope.profiles = []; 8 | $scope.associations = []; 9 | $scope.associationIds = []; 10 | $scope.show = false; 11 | $scope.profile = null; 12 | $scope.profileId = null; 13 | $scope.newProfile = '-1'; 14 | 15 | $scope.showProfileList = false; 16 | 17 | $scope.$on('delete', function (event, profile, profiles, associations) { 18 | $scope.profile = profile; 19 | $scope.profileId = profile['profileId']; 20 | $scope.showProfileList = (associations.length > 0 && associations.length < 5); 21 | $scope.profiles = profiles; 22 | 23 | $scope.associations = associations; 24 | $scope.associationIds = jQuery.map(associations, function (item) { 25 | return item['blog_id']; 26 | }).join(','); 27 | 28 | $scope.show = true; 29 | }); 30 | 31 | /** 32 | * Filter out the current active profile. 33 | * 34 | * @param value 35 | * @param index 36 | * @param array 37 | * 38 | * return {boolean} 39 | */ 40 | $scope.newProfileFilter = function (value, index, array) { 41 | return (value != $scope.activeProfile); 42 | }; 43 | 44 | /** 45 | * Cancel the delete process. 46 | */ 47 | $scope.cancel = function () { 48 | $scope.show = false; 49 | }; 50 | 51 | /** 52 | * Confirm the delete process. 53 | */ 54 | $scope.confirm = function () { 55 | // if the profile is new, remove it and do not trigger the server 56 | if (-1 == $scope.profileId) { 57 | return; 58 | } 59 | 60 | if (null == $scope.profileId) { 61 | $rootScope.$broadcast('remove-profile', $scope.profile); 62 | $scope.show = false; 63 | return; 64 | } 65 | 66 | PersistService.assignProfile($scope.associations, $scope.newProfile).then(function () { 67 | // if the profile has already been persisted, remove it from the server 68 | PersistService.removeProfile($scope.profileId).then(function (result) { 69 | BrowserService.reload(); 70 | }); 71 | }); 72 | }; 73 | } 74 | })(); -------------------------------------------------------------------------------- /src/plug-in/Authentication/SingleSignOn/Profile/Matcher.php: -------------------------------------------------------------------------------- 1 | 9 | * 10 | * @access 11 | * @since 2.2.0 12 | */ 13 | class Matcher 14 | { 15 | /** 16 | * a profile found by a NETBIOS realm 17 | */ 18 | const NETBIOS = "netbios"; 19 | 20 | /** 21 | * a profile found by an account_suffix 22 | */ 23 | const UPN_SUFFIX = "upn_suffix"; 24 | 25 | /** 26 | * a profile found by its Kerberos realm 27 | */ 28 | const KERBEROS_REALM = "kerberos_realm"; 29 | 30 | /** 31 | * by default, we assume that the UPN suffix is used 32 | * @var string 33 | */ 34 | private $type = self::UPN_SUFFIX; 35 | 36 | /** 37 | * profile assigned 38 | * @var array|null 39 | */ 40 | private $profile = null; 41 | 42 | /** 43 | * @param $profile 44 | */ 45 | public function __construct($profile) 46 | { 47 | $this->profile = $profile; 48 | } 49 | 50 | /** 51 | * Sets the type of the match 52 | * 53 | * @param $type 54 | * @return $this 55 | * @throws \Exception If the given type is non of NETBIOS, UPN_SUFFIX or KERBEROS_REALM 56 | */ 57 | public function setType($type) 58 | { 59 | if (!in_array($type, array(self::NETBIOS, self::UPN_SUFFIX, self::KERBEROS_REALM))) { 60 | throw new \Exception("Unknown type"); 61 | } 62 | 63 | $this->type = $type; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getType() 71 | { 72 | return $this->type; 73 | } 74 | 75 | /** 76 | * @return array|null 77 | */ 78 | public function getProfile() 79 | { 80 | return $this->profile; 81 | } 82 | 83 | /** 84 | * Wither which creates a new Matcherwith provided type 85 | * @param $type 86 | * @return $this 87 | * @throws \Exception 88 | */ 89 | public function withType($type) 90 | { 91 | $r = new Matcher($this->profile); 92 | 93 | return $r->setType($type); 94 | } 95 | 96 | public function __toString() 97 | { 98 | return "Match={type='" . $this->type . "'}"; 99 | } 100 | 101 | /** 102 | * Factory methods, which delegates to #withType 103 | * 104 | * @param $profile 105 | * @param $type 106 | * @return static 107 | * @throws \Exception 108 | */ 109 | public static function create($profile, $type) 110 | { 111 | $r = new Matcher($profile); 112 | return $r->withType($type); 113 | } 114 | 115 | /** 116 | * Creates a null instance 117 | * @return null 118 | */ 119 | public static function noMatch() 120 | { 121 | return null; 122 | } 123 | } -------------------------------------------------------------------------------- /js/libraries/ng-notify.min.js: -------------------------------------------------------------------------------- 1 | !function(){"use strict";var a=angular.module("ngNotify",[]),b=!1;try{angular.module("ngSanitize")&&(angular.module("ngNotify").requires.push("ngSanitize"),b=!0)}catch(c){}var d='
×
';a.run(["$templateCache",function(a){a.put("templates/ng-notify/ng-notify.html",d)}]),a.provider("ngNotify",function(){this.$get=["$document","$compile","$log","$rootScope","$timeout","$interval","$templateCache",function(a,c,d,e,f,g,h){var i,j,k="",l=" ",m=3e3,n="ngn-sticky",o=1,p=-1,q=200,r=500,s=25,t=0,u=1,v={theme:"pure",position:"bottom",duration:m,type:"info",sticky:!1,button:!0,html:!1},w={notifyClass:"",notifyMessage:"",notifyStyle:{display:"none",opacity:t}},x={pure:k,prime:"ngn-prime",pastel:"ngn-pastel",pitchy:"ngn-pitchy"},y={info:"ngn-info",error:"ngn-error",success:"ngn-success",warn:"ngn-warn",grimace:"ngn-grimace"},z={bottom:"ngn-bottom",top:"ngn-top"},A=e.$new(),B=c(h.get("templates/ng-notify/ng-notify.html"))(A);A.ngNotify=angular.extend({},w),f(function(){a.find("body").append(B)});var C=function(a){var b=a.type||v.type;return(y[b]||y.info)+l},D=function(a){var b=a.theme||v.theme;return(x[b]||x.pure)+l},E=function(a){var b=a.position||v.position;return(z[b]||z.bottom)+l},F=function(a){var b=a.duration||v.duration;return angular.isNumber(b)?b:m},G=function(a){var b=void 0!==a.sticky?a.sticky:v.sticky;return b?!0:!1},H=function(a,b){var c=void 0!==a.button?a.button:v.button;return c&&b},I=function(a){if((a.html||v.html)&&!b)return d.debug("ngNotify warning: \ngSanitize couldn't be located. In order to use the 'html' option, be sure the ngSanitize source is included in your project."),!1;var c=void 0!==a.html?a.html:v.html;return c?!0:!1},J=function(a,b){var c=C(a)+D(a)+E(a);return c+=b?n:k},K=function(){A.ngNotify=angular.extend({},w)},L=function(a,b,c,d){var e=s/c;A.ngNotify.notifyStyle={display:"block",opacity:b};var f=function(){b+=a*e,A.ngNotify.notifyStyle.opacity=b,(t>=b||b>=u)&&(g.cancel(j),j=!1,d())};j||(j=g(f,s))},M=function(a,b){L(p,u,a,b)},N=function(a,b){L(o,t,a,b)};return A.dismiss=function(){M(r,function(){K()})},{config:function(a){a=a||{},angular.extend(v,a)},set:function(a,b){if(a){g.cancel(j),j=!1,f.cancel(i);var c={};"object"==typeof b?c=b:c.type=b;var d=G(c),e=F(c);angular.extend(A.ngNotify,{notifyHtml:I(c),notifyClass:J(c,d),notifyButton:H(c,d),notifyMessage:a}),N(q,function(){d||(i=f(function(){A.dismiss()},e))})}},dismiss:function(){A.dismiss()},addTheme:function(a,b){a&&b&&(x[a]=b)},addType:function(a,b){a&&b&&(y[a]=b)}}}]})}(); 2 | //# sourceMappingURL=ng-notify.min.js.map -------------------------------------------------------------------------------- /src/plug-in/Multisite/Site/Ui/ExtendSiteList.php: -------------------------------------------------------------------------------- 1 | 13 | * @access public 14 | */ 15 | class ExtendSiteList 16 | { 17 | const ADI_PROFILE_COLUMN = 'adi-profile'; 18 | 19 | /** 20 | * @var BlogConfigurationRepository 21 | */ 22 | private $blogConfigurationRepository; 23 | 24 | /** 25 | * @var ProfileRepository 26 | */ 27 | private $profileRepository; 28 | 29 | /** 30 | * @param BlogConfigurationRepository $blogConfigurationRepository 31 | * @param ProfileRepository $profileRepository 32 | */ 33 | public function __construct(BlogConfigurationRepository $blogConfigurationRepository, 34 | ProfileRepository $profileRepository 35 | ) 36 | { 37 | $this->blogConfigurationRepository = $blogConfigurationRepository; 38 | $this->profileRepository = $profileRepository; 39 | } 40 | 41 | /** 42 | * Add an 'user is disabled' indicator on the user management screen. 43 | */ 44 | public function register() 45 | { 46 | add_filter('wpmu_blogs_columns', array($this, 'addColumns'), 10, 1); 47 | add_action('manage_sites_custom_column', array($this, 'addContent'), 1, 2); 48 | } 49 | 50 | /** 51 | * Add ADI profile column 52 | * 53 | * @param array $columns 54 | * 55 | * @return array 56 | */ 57 | public function addColumns($columns) 58 | { 59 | $columns[self::ADI_PROFILE_COLUMN] = __('Active NADI profile', 'next-active-directory-integration'); 60 | 61 | return $columns; 62 | } 63 | 64 | /** 65 | * Add the ADI profile for the current blog 66 | * 67 | * @param string $columnName 68 | * @param int $blogId 69 | * 70 | * @return string 71 | */ 72 | public function addContent($columnName, $blogId) 73 | { 74 | if ($columnName == self::ADI_PROFILE_COLUMN) { 75 | $id = $this->blogConfigurationRepository->findProfileId($blogId); 76 | $isDefaultProfileUsed = $this->blogConfigurationRepository->isDefaultProfileUsed($blogId); 77 | 78 | if ($id) { 79 | $name = $this->profileRepository->findName($id, ''); 80 | 81 | if ($isDefaultProfileUsed) { 82 | echo sprintf(__('%s (default profile)', 'next-active-directory-integration'), $name); 83 | 84 | return; 85 | } 86 | 87 | if ($name) { 88 | echo $name; 89 | 90 | return; 91 | } 92 | } 93 | 94 | echo "" . __('None assigned', 'next-active-directory-integration') . ''; 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Validator/Rule/DisallowInvalidWordPressRoles.php: -------------------------------------------------------------------------------- 1 | 18 | * @author Sebastian Weinert 19 | * @author Danny Meißner 20 | * 21 | * @access 22 | */ 23 | class DisallowInvalidWordPressRoles extends RuleAdapter 24 | { 25 | /** 26 | * Validate the given data. 27 | * 28 | * @param string $value 29 | * @param array $data 30 | * 31 | * @return mixed 32 | */ 33 | public function validate($value, $data) 34 | { 35 | $wpRoles = $this->getWpRoles($value); 36 | 37 | // check for invalid roles 38 | if ($this->containsInvalidRoles($wpRoles)) { 39 | $msg = $this->getMsg(); 40 | return $msg[1]; 41 | } 42 | 43 | // check for super user in blog 44 | if ($this->isOnNetworkDashboard()) { 45 | return true; 46 | } 47 | 48 | if (ArrayUtil::containsIgnoreCase(Manager::ROLE_SUPER_ADMIN, $wpRoles)) { 49 | $msg = $this->getMsg(); 50 | return array(Type::ERROR => $msg[Type::ERROR][0]); 51 | } 52 | 53 | // all ok 54 | return true; 55 | } 56 | 57 | /** 58 | * Check if all @param $roles exist in the WordPress instance. 59 | * 60 | * @param $roles 61 | * @return bool 62 | */ 63 | protected function containsInvalidRoles($roles) 64 | { 65 | $wpRoles = new \WP_Roles(); 66 | foreach ($roles as $role) { 67 | if (!$wpRoles->is_role($role) && $role !== Manager::ROLE_SUPER_ADMIN) { 68 | return true; 69 | } 70 | } 71 | 72 | return false; 73 | } 74 | 75 | /** 76 | * Convert the given list string and retrieve all WordPress roles. 77 | * 78 | * @param $value 79 | * 80 | * @return array 81 | */ 82 | protected function getWpRoles($value) 83 | { 84 | // convert the given string into separate lines 85 | $roleMappings = StringUtil::split($value, ';'); 86 | // remove empty values from the array 87 | $roleMappings = array_filter($roleMappings); 88 | 89 | return array_map(function ($roleMappingString) { 90 | $roleMapping = StringUtil::split($roleMappingString, '='); 91 | 92 | return $roleMapping[1]; 93 | }, $roleMappings); 94 | } 95 | 96 | /** 97 | * Check if the user is currently on the network Dashboard. 98 | * 99 | * @return bool 100 | */ 101 | protected function isOnNetworkDashboard() 102 | { 103 | return Util::isOnNetworkDashboard(); 104 | } 105 | } -------------------------------------------------------------------------------- /js/app/profile-options/controllers/security.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('SecurityController', SecurityController); 3 | 4 | SecurityController.$inject = ['$scope', 'ListService', 'DataService']; 5 | 6 | function SecurityController($scope, ListService, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.$on('permissionItems', function (event, data) { 12 | $scope.permissionOptions = data; 13 | }); 14 | 15 | $scope.$on('options', function (event, data) { 16 | $scope.option = { 17 | enable_smartcard_user_login: $valueHelper.findValue("enable_smartcard_user_login", data), 18 | custom_login_page_enabled: $valueHelper.findValue("custom_login_page_enabled", data), 19 | custom_login_page_uri: $valueHelper.findValue("custom_login_page_uri", data), 20 | allow_xmlrpc_login: $valueHelper.findValue("allow_xmlrpc_login", data) 21 | }; 22 | 23 | if ($valueHelper.findValue("domain_sid", data) == '') { 24 | $scope.isSaveDisabled = true; 25 | } else { 26 | $scope.isSaveDisabled = false; 27 | } 28 | 29 | $scope.permission = { 30 | enable_smartcard_user_login: $valueHelper.findPermission("enable_smartcard_user_login", data), 31 | custom_login_page_enabled: $valueHelper.findPermission("custom_login_page_enabled", data), 32 | custom_login_page_uri: $valueHelper.findPermission("custom_login_page_uri", data), 33 | allow_xmlrpc_login: $valueHelper.findPermission("allow_xmlrpc_login", data) 34 | }; 35 | }); 36 | 37 | $scope.$on('validation', function (event, data) { 38 | $scope.messages = { 39 | enable_smartcard_user_login: $valueHelper.findMessage("enable_smartcard_user_login", data), 40 | custom_login_page_enabled: $valueHelper.findMessage("custom_login_page_enabled", data), 41 | custom_login_page_uri: $valueHelper.findMessage("custom_login_page_uri", data), 42 | allow_xmlrpc_login: $valueHelper.findMessage("allow_xmlrpc_login", data) 43 | }; 44 | }); 45 | 46 | $scope.$on('verification', function (event, data) { 47 | $scope.isSaveDisabled = false; 48 | }); 49 | 50 | $scope.getPreparedOptions = function () { 51 | var data = DataService.cleanOptions($scope.option); 52 | return data; 53 | }; 54 | 55 | $scope.containsErrors = function () { 56 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 57 | }; 58 | 59 | $scope.save = function() { 60 | $scope.$parent.save(); 61 | }; 62 | } 63 | })(); -------------------------------------------------------------------------------- /js/libraries/angular-busy.min.js: -------------------------------------------------------------------------------- 1 | /*** An AngularJS module for reacting to when your app is busy. 2 | * @author Mike Grabski 3 | * @version v0.2.0 4 | * @link https://github.com/HackedByChinese/ng-busy.git 5 | * @license MIT 6 | */ 7 | !function(a,b){"use strict";b.module("ngBusy.interceptor",[]).provider("busyInterceptor",function(){this.$get=["$rootScope","$q",function(a,b){function c(){e=f=0}function d(b){b.config.notBusy||(a.$broadcast("busy.end",{url:b.config.url,name:b.config.name,remaining:e-++f}),f>=e&&c())}var e=0,f=0;return{outstanding:function(){return e-f},request:function(c){return c.notBusy||(a.$broadcast("busy.begin",{url:c.url,name:c.name}),e++),c||b.when(c)},response:function(a){return d(a),a},responseError:function(a){return d(a),b.reject(a)}}}]}).config(["$httpProvider",function(a){a.interceptors.push("busyInterceptor")}]),b.module("ngBusy.busy",[]).directive("busy",["$parse","$timeout",function(a,c){return{restrict:"A",tranclude:!0,scope:{},controller:["$scope",function(a){this.setBusyMessageElement=function(b){a.busyMessageElement=b}}],link:function(d,e,f){f.$observe("busy",function(a){d.busyMessage=a}),f.$observe("busyWhenUrl",function(a){d.busyWhenUrl=a}),f.$observe("busyWhenName",function(a){d.busyWhenName=a}),f.$observe("busyAddClasses",function(a){d.busyAddClasses=a}),f.$observe("busyRemoveClasses",function(a){d.busyRemoveClasses=a}),f.$observe("busyDisabled",function(c){var e=a(c)(d);d.busyDisabled=b.isDefined(e)?e:!0}),f.$observe("notBusyWhenUrl",function(a){d.notBusyWhenUrl=a}),f.$observe("notBusyWhenName",function(a){d.notBusyWhenName=a}),f.$observe("notBusyAddClasses",function(a){d.notBusyAddClasses=a}),f.$observe("notBusyRemoveClasses",function(a){d.notBusyRemoveClasses=a}),f.$observe("notBusyDisabled",function(c){var e=a(c)(d);d.notBusyDisabled=b.isDefined(e)?e:!1}),d.isBusyFor=function(a,b){var c;return d[c=b?"busyWhenName":"notBusyWhenName"]?!!a.name&&!!a.name.match(d[c]):d[c=b?"busyWhenUrl":"notBusyWhenUrl"]?!!a.url&&!!a.url.match(d[c]):b===!0||a.remaining<=0},d.$on("busy.begin",function(a,b){if(!d.busy&&d.isBusyFor(b,!0)){d.originalContent=e.html(),d.busyDisabled&&c(function(){e.attr("disabled",!0)});var f=d.busyMessageElement?d.busyMessageElement.clone():null;(f||d.busyMessage)&&e.html("").append(f||d.busyMessage),e.removeClass(d.busyRemoveClasses).addClass(d.busyAddClasses),d.busy=!0}}),d.$on("busy.end",function(a,b){d.busy&&d.isBusyFor(b)&&(d.originalContent&&e.html(d.originalContent),e.attr("disabled",d.notBusyDisabled===!0),e.removeClass(d.notBusyRemoveClasses).addClass(d.notBusyAddClasses),d.busy=!1)})}}}]).directive("busyMessage",function(){return{restrict:"AE",transclude:!0,require:"^busy",template:"",replace:!0,compile:function(a,b,c){return function(a,b,d,e){e.setBusyMessageElement(c(a,function(){}))}}}}),b.module("ngBusy",["ngBusy.interceptor","ngBusy.busy"])}(window,window.angular); 8 | //# sourceMappingURL=angular-busy.map -------------------------------------------------------------------------------- /js/app/blog-options/controllers/sso.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('SsoController', SsoController); 3 | 4 | SsoController.$inject = ['$scope', 'ListService', 'DataService']; 5 | 6 | function SsoController($scope, ListService, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.permissionOptions = DataService.getPermissionOptions(); 12 | 13 | $scope.$on('options', function (event, data) { 14 | $scope.option = { 15 | sso: $valueHelper.findValue("sso", data), 16 | sso_user: $valueHelper.findValue("sso_user", data), 17 | sso_password: $valueHelper.findValue("sso_password", data), 18 | sso_environment_variable: $valueHelper.findValue("sso_environment_variable", data), 19 | sso_disable_for_xmlrpc: $valueHelper.findValue("sso_disable_for_xmlrpc", data), 20 | kerberos_realm_mappings: $valueHelper.findValue("kerberos_realm_mappings", data) 21 | }; 22 | 23 | if ($valueHelper.findValue("domain_sid", data) == '') { 24 | $scope.isSaveDisabled = true; 25 | } 26 | 27 | $scope.permission = { 28 | sso: $valueHelper.findPermission("sso", data), 29 | sso_user: $valueHelper.findPermission("sso_user", data), 30 | sso_password: $valueHelper.findPermission("sso_password", data), 31 | sso_environment_variable: $valueHelper.findPermission("sso_environment_variable", data), 32 | sso_disable_for_xmlrpc: $valueHelper.findPermission("sso_disable_for_xmlrpc", data), 33 | kerberos_realm_mappings: $valueHelper.findPermission("kerberos_realm_mappings", data) 34 | }; 35 | }); 36 | 37 | $scope.$on('validation', function (event, data) { 38 | $scope.messages = { 39 | sso: $valueHelper.findMessage("sso", data), 40 | sso_user: $valueHelper.findMessage("sso_user", data), 41 | sso_password: $valueHelper.findMessage("sso_password", data), 42 | sso_environment_variable: $valueHelper.findMessage("sso_environment_variable", data), 43 | kerberos_realm_mappings: $valueHelper.findMessage("kerberos_realm_mappings", data) 44 | }; 45 | }); 46 | 47 | $scope.$on('verification', function (event, data) { 48 | $scope.isSaveDisabled = false; 49 | }); 50 | 51 | $scope.getPreparedOptions = function () { 52 | var data = DataService.cleanOptions($scope.option); 53 | return data; 54 | }; 55 | 56 | $scope.containsErrors = function () { 57 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 58 | }; 59 | 60 | $scope.save = function() { 61 | $scope.$parent.save(); 62 | }; 63 | } 64 | })(); -------------------------------------------------------------------------------- /src/shared/Util/Session/SessionHandler.php: -------------------------------------------------------------------------------- 1 | 12 | * @author Danny Meißner 13 | * 14 | * @access 15 | */ 16 | class SessionHandler 17 | { 18 | /** 19 | * @var SessionHandler 20 | */ 21 | private static $instance = null; 22 | 23 | private function __construct() 24 | { 25 | } 26 | 27 | private function __clone() 28 | { 29 | } 30 | 31 | /** 32 | * Get the singleton instance of {@link SessionHandler}. 33 | * 34 | * @return SessionHandler 35 | */ 36 | public static function getInstance() 37 | { 38 | $args = func_get_args(); 39 | $instance = self::$instance; 40 | 41 | if (sizeof($args) > 0) { 42 | $instance = $args[0]; 43 | } 44 | 45 | if ($instance == null) { 46 | // create new instance 47 | self::$instance = new SessionHandler(); 48 | self::$instance->startSession(); 49 | } else { 50 | // get instance from parameter 51 | self::$instance = $instance; 52 | } 53 | 54 | return self::$instance; 55 | } 56 | 57 | /** 58 | * Save the given value in the session using the next_ad_int_ prefix and {@code $key} as key. 59 | * 60 | * @param $key 61 | * @param $value 62 | */ 63 | public function setValue($key, $value) 64 | { 65 | $sessionKey = $this->normalizeKey($key); 66 | $_SESSION[$sessionKey] = $value; 67 | } 68 | 69 | /** 70 | * Retrieve the value from the session using the next_ad_int_ prefix and {@code $key} as key. 71 | * 72 | * @param $key 73 | * @param null $default 74 | * 75 | * @return null 76 | */ 77 | public function getValue($key, $default = null) 78 | { 79 | $sessionKey = $this->normalizeKey($key); 80 | 81 | if (!isset($_SESSION[$sessionKey])) { 82 | return $default; 83 | } 84 | 85 | return $_SESSION[$sessionKey]; 86 | } 87 | 88 | /** 89 | * Remove an entry from the session. 90 | * 91 | * @param $key 92 | */ 93 | public function clearValue($key) 94 | { 95 | $sessionKey = $this->normalizeKey($key); 96 | 97 | unset($_SESSION[$sessionKey]); 98 | } 99 | 100 | /** 101 | * Return the normalized key. 102 | * 103 | * @param $key 104 | * 105 | * @return string 106 | */ 107 | protected function normalizeKey($key) 108 | { 109 | if (StringUtil::startsWith(NEXT_ACTIVE_DIRECTORY_INTEGRATION_PREFIX, $key)) { 110 | return $key; 111 | } 112 | 113 | return NEXT_ACTIVE_DIRECTORY_INTEGRATION_PREFIX . $key; 114 | } 115 | 116 | /** 117 | * Check if the session is not started and start it. 118 | */ 119 | protected function startSession() 120 | { 121 | $native = Util::native(); 122 | 123 | if ('' === $native->getSessionId()) { 124 | $native->startSession(); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /js/app/profile-options/controllers/credential.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('PasswordController', PasswordController); 3 | 4 | PasswordController.$inject = ['$scope', 'DataService']; 5 | 6 | function PasswordController($scope, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.$on('permissionItems', function (event, data) { 12 | $scope.permissionOptions = data; 13 | }); 14 | 15 | $scope.$on('options', function (event, data) { 16 | $scope.option = { 17 | no_random_password: $valueHelper.findValue("no_random_password", data), 18 | enable_password_change: $valueHelper.findValue("enable_password_change", data), 19 | fallback_to_local_password: $valueHelper.findValue("fallback_to_local_password", data), 20 | auto_update_password: $valueHelper.findValue("auto_update_password", data), 21 | enable_lost_password_recovery: $valueHelper.findValue("enable_lost_password_recovery", data), 22 | }; 23 | 24 | if ($valueHelper.findValue("domain_sid", data) == '') { 25 | $scope.isSaveDisabled = true; 26 | } else { 27 | $scope.isSaveDisabled = false; 28 | } 29 | 30 | $scope.permission = { 31 | no_random_password: $valueHelper.findPermission("no_random_password", data), 32 | enable_password_change: $valueHelper.findPermission("enable_password_change", data), 33 | fallback_to_local_password: $valueHelper.findPermission("fallback_to_local_password", data), 34 | auto_update_password: $valueHelper.findPermission("auto_update_password", data), 35 | enable_lost_password_recovery: $valueHelper.findPermission("enable_lost_password_recovery", data), 36 | }; 37 | }); 38 | 39 | $scope.$on('validation', function (event, data) { 40 | $scope.messages = { 41 | no_random_password: $valueHelper.findMessage("no_random_password", data), 42 | enable_password_change: $valueHelper.findMessage("enable_password_change", data), 43 | fallback_to_local_password: $valueHelper.findMessage("fallback_to_local_password", data), 44 | auto_update_password: $valueHelper.findMessage("auto_update_password", data), 45 | enable_lost_password_recovery: $valueHelper.findMessage("enable_lost_password_recovery", data) 46 | }; 47 | }); 48 | 49 | $scope.$on('verification', function (event, data) { 50 | $scope.isSaveDisabled = false; 51 | }); 52 | 53 | $scope.getPreparedOptions = function () { 54 | var data = DataService.cleanOptions($scope.option); 55 | return data; 56 | }; 57 | 58 | $scope.containsErrors = function () { 59 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 60 | }; 61 | } 62 | })(); -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/View/Page/PageAdapter.php: -------------------------------------------------------------------------------- 1 | 11 | * @author Sebastian Weinert 12 | * 13 | * @access public 14 | */ 15 | abstract class PageAdapter implements Page 16 | { 17 | /** @var TwigContainer */ 18 | protected $twigContainer; 19 | 20 | /** 21 | * Adi_Page_PageAbstract constructor. 22 | * 23 | * @param TwigContainer $twigContainer 24 | */ 25 | protected function __construct(TwigContainer $twigContainer) 26 | { 27 | $this->twigContainer = $twigContainer; 28 | } 29 | 30 | /** 31 | * Render the provided data as JSON and exit 32 | * 33 | * @param mixed $data 34 | */ 35 | protected function renderJson($data) 36 | { 37 | $this->display('ajax-json.twig', array('data' => $data)); 38 | exit; 39 | } 40 | 41 | /** 42 | * Check if the user has the permission to view this page and either send a message or render the page. 43 | * 44 | * @param string $template 45 | * @param array $params 46 | * 47 | * @return string 48 | */ 49 | protected function render($template, $params = array()) 50 | { 51 | // check if the user is able to view this site 52 | $this->checkCapability(); 53 | 54 | // get twig and render the display 55 | $twig = $this->twigContainer->getTwig(); 56 | 57 | return $twig->render($template, $params); 58 | } 59 | 60 | /** 61 | * Get the rendered template and display it. 62 | * 63 | * @param $template 64 | * @param array $params 65 | */ 66 | protected function display($template, $params = array()) 67 | { 68 | echo $this->render($template, $params); 69 | } 70 | 71 | /** 72 | * Check if the current user can see this page. 73 | * If not an error will be shown. 74 | */ 75 | protected function checkCapability() 76 | { 77 | if (!$this->currentUserHasCapability()) { 78 | $message = esc_html__('You do not have sufficient permissions to access this page.', 'next-active-directory-integration'); 79 | wp_die($message); 80 | } 81 | } 82 | 83 | /** 84 | * @see check_ajax_referer() 85 | */ 86 | protected function checkNonce() 87 | { 88 | check_ajax_referer($this->getNonce(), 'security', true); 89 | } 90 | 91 | /** 92 | * Check if the current user has the given capability. 93 | * 94 | * @return bool 95 | */ 96 | protected function currentUserHasCapability() 97 | { 98 | return current_user_can($this->getCapability()); 99 | } 100 | 101 | /** 102 | * Get the current capability to check if the user has permission to view this page. 103 | * 104 | * @return string 105 | */ 106 | protected abstract function getCapability(); 107 | 108 | /** 109 | * Get the current nonce value. 110 | * 111 | * @return mixed 112 | */ 113 | protected abstract function getNonce(); 114 | } -------------------------------------------------------------------------------- /js/app/blog-options/controllers/credential.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('PasswordController', PasswordController); 3 | 4 | PasswordController.$inject = ['$scope', 'DataService']; 5 | 6 | function PasswordController($scope, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.permissionOptions = DataService.getPermissionOptions(); 12 | 13 | $scope.$on('options', function (event, data) { 14 | $scope.option = { 15 | no_random_password: $valueHelper.findValue("no_random_password", data), 16 | enable_password_change: $valueHelper.findValue("enable_password_change", data), 17 | fallback_to_local_password: $valueHelper.findValue("fallback_to_local_password", data), 18 | auto_update_password: $valueHelper.findValue("auto_update_password", data), 19 | enable_lost_password_recovery: $valueHelper.findValue("enable_lost_password_recovery", data), 20 | }; 21 | 22 | if ($valueHelper.findValue("domain_sid", data) == '') { 23 | $scope.isSaveDisabled = true; 24 | } 25 | 26 | $scope.permission = { 27 | no_random_password: $valueHelper.findPermission("no_random_password", data), 28 | enable_password_change: $valueHelper.findPermission("enable_password_change", data), 29 | fallback_to_local_password: $valueHelper.findPermission("fallback_to_local_password", data), 30 | auto_update_password: $valueHelper.findPermission("auto_update_password", data), 31 | enable_lost_password_recovery: $valueHelper.findPermission("enable_lost_password_recovery", data), 32 | verification_username : $valueHelper.findPermission("verification_username", data), 33 | verification_password : $valueHelper.findPermission("verification_password", data) 34 | }; 35 | }); 36 | 37 | $scope.$on('validation', function (event, data) { 38 | $scope.messages = { 39 | no_random_password: $valueHelper.findMessage("no_random_password", data), 40 | enable_password_change: $valueHelper.findMessage("enable_password_change", data), 41 | fallback_to_local_password: $valueHelper.findMessage("fallback_to_local_password", data), 42 | auto_update_password: $valueHelper.findMessage("auto_update_password", data), 43 | enable_lost_password_recovery: $valueHelper.findMessage("enable_lost_password_recovery", data) 44 | }; 45 | }); 46 | 47 | $scope.$on('verification', function (event, data) { 48 | $scope.isSaveDisabled = false; 49 | }); 50 | 51 | $scope.getPreparedOptions = function () { 52 | return DataService.cleanOptions($scope.option); 53 | }; 54 | 55 | $scope.containsErrors = function () { 56 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 57 | }; 58 | } 59 | })(); -------------------------------------------------------------------------------- /js/app/profile-options/controllers/sso.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('SsoController', SsoController); 3 | 4 | SsoController.$inject = ['$scope', 'ListService', 'DataService']; 5 | 6 | function SsoController($scope, ListService, DataService) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.$on('permissionItems', function (event, data) { 12 | $scope.permissionOptions = data; 13 | }); 14 | 15 | $scope.$on('options', function (event, data) { 16 | $scope.option = { 17 | sso: $valueHelper.findValue("sso", data), 18 | sso_user: $valueHelper.findValue("sso_user", data), 19 | sso_password: $valueHelper.findValue("sso_password", data), 20 | sso_environment_variable: $valueHelper.findValue("sso_environment_variable", data), 21 | sso_disable_for_xmlrpc: $valueHelper.findValue("sso_disable_for_xmlrpc", data), 22 | kerberos_realm_mappings: $valueHelper.findValue("kerberos_realm_mappings", data) 23 | }; 24 | 25 | if ($valueHelper.findValue("domain_sid", data) == '') { 26 | $scope.isSaveDisabled = true; 27 | } else { 28 | $scope.isSaveDisabled = false; 29 | } 30 | 31 | $scope.permission = { 32 | sso: $valueHelper.findPermission("sso", data), 33 | sso_user: $valueHelper.findPermission("sso_user", data), 34 | sso_password: $valueHelper.findPermission("sso_password", data), 35 | sso_environment_variable: $valueHelper.findPermission("sso_environment_variable", data), 36 | sso_disable_for_xmlrpc: $valueHelper.findPermission("sso_disable_for_xmlrpc", data), 37 | kerberos_realm_mappings: $valueHelper.findPermission("kerberos_realm_mappings", data) 38 | }; 39 | }); 40 | 41 | $scope.$on('validation', function (event, data) { 42 | $scope.messages = { 43 | sso: $valueHelper.findMessage("sso", data), 44 | sso_user: $valueHelper.findMessage("sso_user", data), 45 | sso_password: $valueHelper.findMessage("sso_password", data), 46 | sso_environment_variable: $valueHelper.findMessage("sso_environment_variable", data), 47 | kerberos_realm_mappings: $valueHelper.findMessage("kerberos_realm_mappings", data) 48 | }; 49 | }); 50 | 51 | $scope.$on('verification', function (event, data) { 52 | $scope.isSaveDisabled = false; 53 | }); 54 | 55 | $scope.getPreparedOptions = function () { 56 | var data = DataService.cleanOptions($scope.option); 57 | return data; 58 | }; 59 | 60 | $scope.containsErrors = function () { 61 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 62 | }; 63 | 64 | $scope.save = function() { 65 | $scope.$parent.save(); 66 | }; 67 | } 68 | })(); -------------------------------------------------------------------------------- /src/plug-in/Role/Mapping.php: -------------------------------------------------------------------------------- 1 | 9 | * @access public 10 | */ 11 | class Mapping 12 | { 13 | /** 14 | * @var array 15 | */ 16 | private $securityGroups = array(); 17 | 18 | /** 19 | * @var array 20 | */ 21 | private $wordPressRoles = array(); 22 | 23 | /** 24 | * @var string 25 | */ 26 | private $guidOrUsername; 27 | 28 | /** 29 | * @param string $guidOrUsername 30 | */ 31 | public function __construct($guidOrUsername) 32 | { 33 | $this->guidOrUsername = $guidOrUsername; 34 | } 35 | 36 | /** 37 | * Set the Active Directory security groups 38 | * 39 | * @param array $securityGroups 40 | */ 41 | public function setSecurityGroups($securityGroups) 42 | { 43 | if (!is_array($securityGroups)) { 44 | $securityGroups = array(); 45 | } 46 | 47 | $this->securityGroups = $securityGroups; 48 | } 49 | 50 | /** 51 | * Get Active Directory security groups 52 | * @return array 53 | */ 54 | public function getSecurityGroups() 55 | { 56 | return $this->securityGroups; 57 | } 58 | 59 | /** 60 | * Set the user's WordPress roles 61 | * 62 | * @param array $wordPressRoles 63 | */ 64 | public function setWordPressRoles($wordPressRoles) 65 | { 66 | if (!is_array($wordPressRoles)) { 67 | $wordPressRoles = array(); 68 | } 69 | 70 | $this->wordPressRoles = $wordPressRoles; 71 | } 72 | 73 | /** 74 | * Get the user's WordPress roles 75 | * @return array 76 | */ 77 | public function getWordPressRoles() 78 | { 79 | return $this->wordPressRoles; 80 | } 81 | 82 | 83 | /** 84 | * Does the current mapping belong to the security group? 85 | * 86 | * @param string $securityGroup 87 | * 88 | * @return bool 89 | */ 90 | public function isInSecurityGroup($securityGroup) 91 | { 92 | return in_array($securityGroup, $this->securityGroups); 93 | } 94 | 95 | /** 96 | * Get all security groups which are assigned 97 | * 98 | * @param $securityGroups 99 | * 100 | * @return array elements with matching groups 101 | */ 102 | public function getMatchingGroups($securityGroups) 103 | { 104 | $equalGroups = array_intersect($this->securityGroups, $securityGroups); 105 | 106 | return array_values($equalGroups); 107 | } 108 | 109 | /** 110 | * Merge the given $roleMapping into this object. 111 | * 112 | * @param Mapping $otherRoleMapping 113 | */ 114 | public function merge(Mapping $otherRoleMapping) 115 | { 116 | $this->securityGroups += $otherRoleMapping->getSecurityGroups(); 117 | $this->wordPressRoles += $otherRoleMapping->getWordPressRoles(); 118 | } 119 | 120 | public function __toString() 121 | { 122 | return "Mapping " . $this->guidOrUsername . "={ad_security_groups='" . implode(", ", $this->securityGroups) 123 | . "',wordpress_roles='" . implode(', ', $this->wordPressRoles) . "'}"; 124 | } 125 | } -------------------------------------------------------------------------------- /src/shared/WordPress/Multisite/Ui/BlogConfigurationController.php: -------------------------------------------------------------------------------- 1 | 13 | * @access public 14 | */ 15 | class BlogConfigurationController 16 | { 17 | /* @var BlogConfigurationRepository $blogConfigurationRepository */ 18 | private $blogConfigurationRepository; 19 | 20 | /* @var Provider */ 21 | private $optionProvider; 22 | 23 | /** 24 | * @param BlogConfigurationRepository $blogConfigurationRepository 25 | * @param Provider $optionProvider 26 | */ 27 | public function __construct(BlogConfigurationRepository $blogConfigurationRepository, 28 | Provider $optionProvider 29 | ) 30 | { 31 | $this->blogConfigurationRepository = $blogConfigurationRepository; 32 | $this->optionProvider = $optionProvider; 33 | } 34 | 35 | /** 36 | * Convert the response from the frontend and save it in the database. 37 | * This methods stores the new option values for blog options. 38 | * 39 | * @param array $options 40 | * 41 | * @return array 42 | */ 43 | public function saveBlogOptions($options) 44 | { 45 | try { 46 | $this->saveBlogOptionsInternal($options); 47 | } catch (\Exception $e) { 48 | return array("status_success" => false); 49 | } 50 | 51 | return array("status_success" => true); 52 | 53 | } 54 | 55 | /** 56 | * Convert the response from the frontend and save it in the database. 57 | * This methods stores the new option values for blog options. 58 | * 59 | * @param array $options 60 | */ 61 | protected function saveBlogOptionsInternal($options) 62 | { 63 | foreach ($options as $optionName => $option) { 64 | if (!$this->validateOption($optionName, $option)) { 65 | continue; 66 | } 67 | 68 | $this->persistOption($optionName, $option); 69 | } 70 | } 71 | 72 | /** 73 | * Persist an single option value change from the $_POST response. 74 | * 75 | * @param string $optionName 76 | * @param array $option 77 | * 78 | * @return string 79 | */ 80 | public function persistOption($optionName, $option) 81 | { 82 | $r = $this->blogConfigurationRepository->persistSanitizedValue(get_current_blog_id(), $optionName, $option); 83 | 84 | return $r; 85 | } 86 | 87 | /** 88 | * Validate option. The option name must exist and the option value must be defined. 89 | * 90 | * @param string $optionName 91 | * @param array $option 92 | * 93 | * @return bool 94 | */ 95 | public function validateOption($optionName, $option) 96 | { 97 | if (!isset($option)) { 98 | return false; 99 | } 100 | 101 | if (!$this->optionProvider->existOption($optionName)) { 102 | return false; 103 | } 104 | 105 | return true; 106 | } 107 | } -------------------------------------------------------------------------------- /src/plug-in/Multisite/Ui/MultisiteMenu.php: -------------------------------------------------------------------------------- 1 | 16 | * @author Sebastian Weinert 17 | * 18 | * @access public 19 | */ 20 | class MultisiteMenu extends MenuAdapter 21 | { 22 | /** @var BlogProfileRelationshipPage | Page */ 23 | private $blogProfileRelationshipPage; 24 | 25 | /** @var NadiMultisiteConfigurationPage | Page */ 26 | private $profileConfigurationPage; 27 | 28 | /** 29 | * @param Provider $optionProvider 30 | * @param BlogProfileRelationshipPage $blogProfileRelationshipPage 31 | * @param NadiMultisiteConfigurationPage $nadiMultisiteConfigurationPage 32 | */ 33 | public function __construct(Provider $optionProvider, 34 | BlogProfileRelationshipPage $blogProfileRelationshipPage, 35 | NadiMultisiteConfigurationPage $nadiMultisiteConfigurationPage 36 | ) 37 | { 38 | parent::__construct($optionProvider); 39 | 40 | $this->blogProfileRelationshipPage = $blogProfileRelationshipPage; 41 | $this->profileConfigurationPage = $nadiMultisiteConfigurationPage; 42 | } 43 | 44 | /** 45 | * Register all menu pages. 46 | */ 47 | public function register() 48 | { 49 | add_action(Actions::ADI_MENU_NETWORK_ADMIN_MENU, array($this, 'registerMenu')); 50 | 51 | $this->addAjaxListener($this->blogProfileRelationshipPage); 52 | $this->addAjaxListener($this->profileConfigurationPage); 53 | } 54 | 55 | /** 56 | * Register all pages for the network admin menu 57 | */ 58 | public function registerMenu() 59 | { 60 | $permission = 'manage_network'; 61 | $renderMethodName = 'renderNetwork'; 62 | $networkMenuTitle = esc_html__('Active Directory Integration', 'next-active-directory-integration'); 63 | $networkMenuSlug = $this->blogProfileRelationshipPage->getSlug(); // the header of the group must have the slug of the first item 64 | 65 | // add menu header 66 | add_menu_page($networkMenuTitle, $networkMenuTitle, $permission, $networkMenuSlug); 67 | 68 | // add sub menus 69 | $this->addSubMenu($networkMenuSlug, $permission, $this->blogProfileRelationshipPage, $renderMethodName); 70 | $profileConfigurationPage = $this->addSubMenu($networkMenuSlug, $permission, $this->profileConfigurationPage, $renderMethodName); 71 | 72 | add_action('admin_enqueue_scripts', array($this, 'loadScriptsAndStyle')); 73 | add_action('load-' . $profileConfigurationPage, array($this, 'addHelpTab')); 74 | } 75 | 76 | /** 77 | * Add scripts/css to the network admin menu. 78 | * 79 | * @param $hook 80 | */ 81 | public function loadScriptsAndStyle($hook) 82 | { 83 | $this->blogProfileRelationshipPage->loadNetworkScriptsAndStyle($hook); 84 | $this->profileConfigurationPage->loadNetworkScriptsAndStyle($hook); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/shared/Ldap/UserQuery.php: -------------------------------------------------------------------------------- 1 | 11 | * @access public 12 | * @since 2.0.0 13 | */ 14 | class UserQuery 15 | { 16 | /** 17 | * @var string sAMAccountName, userPrincipalName, netbiosName 18 | */ 19 | private $principal; 20 | 21 | /** 22 | * @var boolean 23 | */ 24 | private $isGuid = false; 25 | 26 | /** 27 | * @var Credentials 28 | */ 29 | private $credentials = null; 30 | 31 | private function __construct(?Credentials $credentials = null, ?string $principal = null) 32 | { 33 | $this->principal = $principal; 34 | $this->credentials = $credentials; 35 | } 36 | 37 | /** 38 | * Mark that the principal is a guid 39 | * @return $this 40 | */ 41 | public function setGuid() 42 | { 43 | $this->isGuid = true; 44 | return $this; 45 | } 46 | 47 | /** 48 | * Return if this query is for a guid 49 | * @return bool 50 | */ 51 | public function isGuid() 52 | { 53 | return $this->isGuid; 54 | } 55 | 56 | /** 57 | * @return mixed|string|null 58 | */ 59 | public function getPrincipal() 60 | { 61 | return $this->principal; 62 | } 63 | 64 | /** 65 | * @return Credentials|null 66 | */ 67 | public function getCredentials() 68 | { 69 | return $this->credentials; 70 | } 71 | 72 | /** 73 | * Creates a new UserQuery instance with the given principal but the same credentials; 74 | * @param string $principal 75 | * @return UserQuery 76 | */ 77 | public function withPrincipal(string $principal) 78 | { 79 | return new UserQuery($this->credentials, $principal); 80 | } 81 | 82 | public function withGuid(string $guid) 83 | { 84 | $r = new UserQuery($this->credentials, $guid); 85 | return $r->setGuid(); 86 | } 87 | 88 | /** 89 | * Factory method to create a new UserQuery based upon a principal 90 | * 91 | * @param string $principal 92 | * @param Credentials|null $credentials 93 | * @return UserQuery 94 | */ 95 | public static function forPrincipal($principal, ?Credentials $credentials = null) 96 | { 97 | return new UserQuery($credentials, $principal); 98 | } 99 | 100 | /** 101 | * Factory method to create a new UserQuery solely based upon a Credentials instance 102 | * @param Credentials $credentials 103 | * @return UserQuery 104 | */ 105 | public static function forCredentials(Credentials $credentials) 106 | { 107 | return new UserQuery($credentials); 108 | } 109 | 110 | /** 111 | * Factory method to create a new UserQuery based upon an objectGuid 112 | * @param string $guid 113 | * @param Credentials|null $credentials 114 | * @return mixed 115 | */ 116 | public static function forGuid($guid, ?Credentials $credentials = null) 117 | { 118 | $r = new UserQuery($credentials, $guid); 119 | return $r->setGuid(); 120 | } 121 | 122 | public function __toString() 123 | { 124 | return "UserQuery={principal='" . $this->principal . "',isGuid='" . $this->isGuid() . "'}"; 125 | } 126 | } -------------------------------------------------------------------------------- /src/shared/Util/Encryption.php: -------------------------------------------------------------------------------- 1 | 16 | * @access public 17 | */ 18 | class Encryption 19 | { 20 | /** @var Logger */ 21 | private $logger; 22 | 23 | public function __construct() 24 | { 25 | $this->logger = NadiLog::getInstance(); 26 | } 27 | 28 | /** 29 | * Return `AUTH_SALT` constant or an empty string if not defined. 30 | * This has been added to make NADI compatible with newer PHP versions and WordPress installation in which `AUTH_SALT` is not defined. 31 | * 32 | * To be able to change the `AUTH_SALT`, one can define a key `NEXT_ACTIVE_DIRECTORY_INTEGRATION_ENCRYPTION_KEY` with the old `AUTH_SALT`. 33 | * 34 | * @issue #164 35 | * @issue #173 36 | * @see https://github.com/NeosIT/active-directory-integration2/issues/164 37 | */ 38 | public static function getSalt() 39 | { 40 | if (defined('NEXT_ACTIVE_DIRECTORY_INTEGRATION_ENCRYPTION_KEY')) { 41 | return NEXT_ACTIVE_DIRECTORY_INTEGRATION_ENCRYPTION_KEY; 42 | } 43 | 44 | if (defined('AUTH_SALT')) { 45 | return AUTH_SALT; 46 | } 47 | 48 | return ''; 49 | } 50 | 51 | /** 52 | * This method will encrypt the $plainText and return the encrypted text. 53 | * 54 | * @param $plainText 55 | * 56 | * @return string 57 | */ 58 | public function encrypt($plainText) 59 | { 60 | $password = 'Next Active Directory Integration' . self::getSalt(); 61 | 62 | try { 63 | $encryptedText = Crypto::encryptWithPassword($plainText, $password); 64 | } catch (\Exception $e) { 65 | // prevent the PHP stack trace display by catching all exception because the stack trace can contain the $password. 66 | $this->logger->warning('Plain text can not be encrypted. ' . $e->getMessage()); 67 | 68 | return false; 69 | } 70 | 71 | return $encryptedText; 72 | } 73 | 74 | /** 75 | * This method will decrypt the $encryptedText and return the plain text. 76 | * 77 | * @param $encryptedText 78 | * 79 | * @return string 80 | */ 81 | public function decrypt($encryptedText) 82 | { 83 | $password = 'Next Active Directory Integration' . self::getSalt(); 84 | 85 | // do not decrypt empty texts 86 | if (!$encryptedText) { 87 | return false; 88 | } 89 | 90 | try { 91 | $plainText = Crypto::decryptWithPassword($encryptedText, $password); 92 | } catch (\Exception $e) { 93 | // prevent the PHP stack trace display by catching all exception because the stack trace can contain the $password. 94 | $this->logger->warning('Encrypted text "' . $encryptedText . '" can not be decrypted. ' . $e->getMessage()); 95 | 96 | return false; 97 | } 98 | 99 | return $plainText; 100 | } 101 | } -------------------------------------------------------------------------------- /src/plug-in/Ui/Menu/MenuAdapter.php: -------------------------------------------------------------------------------- 1 | 16 | * @access public 17 | */ 18 | abstract class MenuAdapter 19 | { 20 | /** 21 | * @var Provider 22 | */ 23 | private $optionProvider; 24 | 25 | /** 26 | * @param Provider $optionProvider 27 | */ 28 | public function __construct(Provider $optionProvider) 29 | { 30 | $this->optionProvider = $optionProvider; 31 | } 32 | 33 | /** 34 | * Register all menu pages. 35 | */ 36 | abstract public function register(); 37 | 38 | /** 39 | * Adds the given ajax listener for the given page. 40 | * 41 | * @access protected 42 | * @param Page $page 43 | * @return bool|true|void 44 | */ 45 | function addAjaxListener($page) 46 | { 47 | if (!$page instanceof Page) { 48 | return false; 49 | } 50 | 51 | $wordPressAjaxListener = 'wpAjaxListener'; 52 | 53 | return add_action(Actions::ADI_MENU_WP_AJAX_PREFIX . $page->wpAjaxSlug(), array( 54 | $page, $wordPressAjaxListener, 55 | )); 56 | } 57 | 58 | /** 59 | * Add help tab. 60 | */ 61 | public function addHelpTab() 62 | { 63 | // gets all the OptionMetaData 64 | $options = $this->optionProvider->getAll(); 65 | $screen = get_current_screen(); 66 | 67 | foreach ($options as $optionName => $option) { 68 | if (!isset($option[Attribute::DETAIL])) { 69 | continue; 70 | } 71 | 72 | $data = $this->generateHelpTabEntry($option, $optionName); 73 | $screen->add_help_tab($data); 74 | } 75 | } 76 | 77 | /** 78 | * Return help tab entry. 79 | * 80 | * @access protected 81 | * @param $screen 82 | * @param array $option 83 | * @param string $optionName 84 | * 85 | * @return array 86 | */ 87 | protected function generateHelpTabEntry($option, $optionName) 88 | { 89 | $title = ArrayUtil::get(Attribute::TITLE, $option, ''); 90 | $detail = ArrayUtil::get(Attribute::DETAIL, $option, ''); 91 | $content = '

' . StringUtil::concat($detail, '
') . '

'; 92 | 93 | return array( 94 | 'id' => $optionName, 95 | 'title' => $title, 96 | 'content' => $content, 97 | ); 98 | } 99 | 100 | /** 101 | * Adds a sub menu to the given main menu. 102 | * 103 | * @access protected 104 | * @param string $mainMenuSlug 105 | * @param mixed $permission 106 | * @param Page $page 107 | * @param string $callbackMethodName 108 | * 109 | * @return bool|false|string 110 | */ 111 | public function addSubMenu($mainMenuSlug, $permission, $page, $callbackMethodName) 112 | { 113 | if (!$page instanceof Page) { 114 | return false; 115 | } 116 | 117 | $pageTitle = $page->getTitle(); 118 | $pageSlug = $page->getSlug(); 119 | 120 | return add_submenu_page($mainMenuSlug, $pageTitle, $pageTitle, $permission, $pageSlug, array($page, $callbackMethodName)); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | check()) { 36 | return; 37 | } 38 | } 39 | 40 | // start plugin 41 | $adiPlugin = new \Dreitier\Nadi\Init(); 42 | 43 | // register basic hooks 44 | register_activation_hook(__FILE__, array($adiPlugin, 'activation')); 45 | register_uninstall_hook(__FILE__, array(\Dreitier\Nadi\Init::class /* static */, 'uninstall')); 46 | 47 | add_action('plugins_loaded', 'next_ad_int_angular_ajax_params_to_post'); 48 | 49 | // #204: register `init` hook to make loading the textdomain possible 50 | add_action('init', array($adiPlugin, '_init')); 51 | 52 | // register any hooks after the plug-in has been activated e.g. to display notices for a migration of options 53 | add_action('admin_init', array($adiPlugin, 'postActivation')); 54 | 55 | // --- Normal Blog / Single Site --- 56 | // execute the plugin and their hooks after the 'plugins_loaded' hook has been called 57 | // so we can use WordPress functions for lazy-loading 58 | add_action('set_current_user', array($adiPlugin, 'run')); 59 | 60 | // --- Active Multisite dashboard --- 61 | // we need to register a second hook to decide if the network dashboard is shown. 62 | // another possible solution would be using the hook 'redirect_network_admin_request' from network/admin.php but 63 | // the loading of the menu happens to early 64 | add_action('set_current_user', array($adiPlugin, 'runMultisite')); 65 | 66 | /** 67 | * Global accessor for Next ADI dependencies. 68 | * You can call this function in your own extensions to gain access to the internals of NADI. 69 | * 70 | * @return \Dreitier\Nadi\Dependencies 71 | */ 72 | function next_ad_int() 73 | { 74 | return \Dreitier\Nadi\Dependencies::getInstance(); 75 | } 76 | 77 | function next_ad_int_logger() 78 | { 79 | return \Dreitier\Nadi\Log\NadiLog::getInstance(); 80 | } 81 | 82 | -------------------------------------------------------------------------------- /js/app/profile-options/controllers/logging.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | app.controller('LoggingController', LoggingController); 3 | 4 | LoggingController.$inject = ['$scope', '$http', 'PersistService', 'ngNotify']; 5 | 6 | function LoggingController($scope, $http, PersistService, ngNotify) { 7 | var vm = this; 8 | 9 | $scope.isSaveDisabled = false; 10 | 11 | $scope.$on('permissionItems', function (event, data) { 12 | $scope.permissionOptions = data; 13 | }); 14 | 15 | 16 | $scope.$on('options', function (event, data) { 17 | $scope.option = { 18 | logger_enable_logging: $valueHelper.findValue("logger_enable_logging", data), 19 | logger_custom_path: $valueHelper.findValue("logger_custom_path", data), 20 | }; 21 | 22 | if ($valueHelper.findValue("domain_sid", data) == '') { 23 | $scope.isSaveDisabled = true; 24 | } else { 25 | $scope.isSaveDisabled = false; 26 | } 27 | 28 | $scope.permission = { 29 | logger_enable_logging: $valueHelper.findPermission("logger_enable_logging", data), 30 | logger_custom_path: $valueHelper.findPermission("logger_custom_path", data), 31 | }; 32 | }); 33 | 34 | $scope.$on('validation', function (event, data) { 35 | $scope.messages = { 36 | logger_enable_logging: $valueHelper.findMessage("logger_enable_logging", data), 37 | logger_custom_path: $valueHelper.findMessage("logger_custom_path", data), 38 | }; 39 | }); 40 | 41 | $scope.$on('verification', function (event, data) { 42 | $scope.isSaveDisabled = false; 43 | }); 44 | 45 | 46 | $scope.containsErrors = function () { 47 | return (!$arrayUtil.containsOnlyNullValues($scope.messages)); 48 | }; 49 | 50 | 51 | $scope.activateLogging = function () { 52 | console.log("Save Logging information"); 53 | console.log($scope.$parent.activeProfile.profileId); 54 | 55 | var data = { 56 | options : { 57 | logger_enable_logging : { 58 | option_value : $scope.option.logger_enable_logging, 59 | option_permission : $scope.permission.logger_enable_logging 60 | }, 61 | logger_custom_path : { 62 | option_value : $scope.option.logger_custom_path, 63 | option_permission : $scope.permission.logger_custom_path 64 | }, 65 | profile_name : { 66 | option_value : $scope.$parent.activeProfile.profileName, 67 | option_permission : undefined 68 | } 69 | }, 70 | profile: $scope.$parent.activeProfile.profileId 71 | }; 72 | 73 | PersistService.persistData(data).then(function (response) { 74 | 75 | if (response.type === 'success') { 76 | ngNotify.set(document['next_ad_int']['saving-success'], 'success'); 77 | 78 | } else { 79 | ngNotify.set(document['next_ad_int']['saving-error'], 'error'); 80 | } 81 | }); 82 | } 83 | 84 | } 85 | })(); -------------------------------------------------------------------------------- /src/plug-in/Authentication/VerificationService.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class VerificationService 19 | { 20 | 21 | /** @var Connection */ 22 | private $ldapConnection; 23 | 24 | /** 25 | * @var Repository 26 | */ 27 | private $ldapAttributeRepository; 28 | /** @var Logger */ 29 | private $logger; 30 | 31 | /** 32 | * @param Connection $ldapConnection 33 | * @param Repository $ldapAttributeRepository 34 | */ 35 | public function __construct(Connection $ldapConnection, 36 | Repository $ldapAttributeRepository 37 | ) 38 | { 39 | $this->ldapConnection = $ldapConnection; 40 | $this->ldapAttributeRepository = $ldapAttributeRepository; 41 | $this->logger = NadiLog::getInstance(); 42 | } 43 | 44 | /** 45 | * Check if the connection to the Active Directory can be established. 46 | * Receive objectSid from user used to authenticate. 47 | * 48 | * @param array $data 49 | * 50 | * @return bool|Sid 51 | */ 52 | public function findActiveDirectoryDomainSid($data) 53 | { 54 | $config = new ConnectionDetails(); 55 | $username = $data["verification_username"]; 56 | 57 | $config->setDomainControllers($data["domain_controllers"]); 58 | $config->setPort($data["port"]); 59 | $config->setEncryption($data["encryption"]); 60 | $config->setAllowSelfSigned($data["allow_self_signed"]); 61 | $config->setNetworkTimeout($data["network_timeout"]); 62 | $config->setBaseDn($data["base_dn"]); 63 | $config->setUsername($username); 64 | $config->setPassword($data["verification_password"]); 65 | 66 | $this->ldapConnection->connect($config); 67 | 68 | $isConnected = $this->ldapConnection->isConnected(); 69 | 70 | if ($isConnected) { 71 | $attributeService = $this->getCustomAttributeService(); 72 | $objectSid = $attributeService->getObjectSid(PrincipalResolver::createCredentials($username)); 73 | 74 | // ADI-412: There *should* be an objectSID as we now fall back from sAMAccountName to userPrincipalName 75 | if (false === $objectSid) { 76 | $this->logger->error("objectSID for AD user '" . $username . "' could not be found. Please check that for this account has been defined a full userPrincipalName including the UPN suffix."); 77 | 78 | return false; 79 | } 80 | 81 | return $objectSid; 82 | } 83 | 84 | return false; 85 | } 86 | 87 | public function findActiveDirectoryNetBiosName($data) 88 | { 89 | $attributeService = $this->getCustomAttributeService(); 90 | $netBIOSname = $attributeService->getnetBiosName($data["verification_username"]); 91 | 92 | if ($netBIOSname) { 93 | return $netBIOSname; 94 | } 95 | 96 | return false; 97 | } 98 | 99 | /** 100 | * Get service for verification process 101 | * 102 | * @return Service 103 | */ 104 | public function getCustomAttributeService() 105 | { 106 | return new \Dreitier\Ldap\Attribute\Service($this->ldapConnection, $this->ldapAttributeRepository); 107 | } 108 | } -------------------------------------------------------------------------------- /src/shared/ActiveDirectory/Sid.php: -------------------------------------------------------------------------------- 1 | 14 | * @access public 15 | */ 16 | class Sid 17 | { 18 | /** 19 | * Binary representation 20 | * @var mixed 21 | */ 22 | private $binary; 23 | 24 | /** 25 | * Formatted as "S-1-"... 26 | * @var string 27 | */ 28 | private $formatted; 29 | 30 | /** 31 | * Hex representation 32 | * @var string 33 | */ 34 | private $hex; 35 | 36 | private function __construct($binary, $formatted, $hex) 37 | { 38 | $this->binary = $binary; 39 | $this->formatted = $formatted; 40 | $this->hex = $hex; 41 | } 42 | 43 | /** 44 | * Get formatted SID as "S-..." 45 | * @return string 46 | */ 47 | public function getFormatted() 48 | { 49 | return $this->formatted; 50 | } 51 | 52 | /** 53 | * Get SID as binary string 54 | * 55 | * @return mixed 56 | */ 57 | public function getBinary() 58 | { 59 | return $this->binary; 60 | } 61 | 62 | /** 63 | * Get SID as hex 64 | * @return string 65 | */ 66 | public function getHex() 67 | { 68 | return $this->hex; 69 | } 70 | 71 | /** 72 | * Based upon this SID, the domain part will be extracted 73 | * 74 | * @return Sid 75 | */ 76 | public function getDomainPartAsSid() 77 | { 78 | // this pattern defined the domain part of a SID 79 | // @see https://en.wikipedia.org/wiki/Security_Identifier 80 | $pattern = '/^(S\-1\-5\-21\-\d+\-\d+\-\d+)+(\-.*)?/'; 81 | 82 | if (preg_match($pattern, $this->formatted, $ret)) { 83 | return self::of(strtoupper($ret[1])); 84 | } 85 | 86 | return NULL; 87 | } 88 | 89 | /** 90 | * Create a new SID instance 91 | * @param $objectSid either binary representation, "S-..." format or hex format 92 | * @return Sid|null null if conversion failed 93 | */ 94 | public static function of($objectSid) 95 | { 96 | $binary = null; 97 | $formatted = null; 98 | $hex = null; 99 | 100 | if (empty($objectSid)) { 101 | $objectSid = ""; 102 | } 103 | 104 | // if the object SID does not start with an S- prefix, it is probably binary encoded 105 | if (StringUtil::startsWith('S-', $objectSid)) { 106 | $hex = AdLdap::sidStringToHex($objectSid); 107 | $binary = hex2bin($hex); 108 | $formatted = $objectSid; 109 | } 110 | // only allow hex parameter 111 | // @see https://stackoverflow.com/a/43600382/2545275 112 | else if (trim($objectSid, '0..9A..Fa..f') == '') { 113 | $hex = $objectSid; 114 | $binary = hex2bin($hex); 115 | $formatted = AdLdap::convertBinarySidToString($binary); 116 | } 117 | // check for binary type 118 | // @see https://stackoverflow.com/a/25344979/2545275 119 | else if (preg_match('~[^\x20-\x7E\t\r\n]~', $objectSid)) { 120 | $binary = $objectSid; 121 | $hex = bin2hex($binary); 122 | $formatted = adLDAP::convertBinarySidToString($objectSid); 123 | } 124 | 125 | if (!$binary || !$formatted || !$hex) { 126 | return null; 127 | } 128 | 129 | return new Sid($binary, $formatted, $hex); 130 | } 131 | 132 | public function __toString() 133 | { 134 | return "SID={objectSID='" . $this->formatted . "'}"; 135 | } 136 | } --------------------------------------------------------------------------------