userdirectory = $userdirectory; $this->mapping_roles = $mapping_roles; } /** * Create instance for specific user directory by id. * * @param int $userdirectoryid User directory id to create CProvisioning instance for. */ public static function forUserDirectoryId($userdirectoryid): self { $userdirectories = API::getApiService('userdirectory')->get([ 'output' => ['userdirectoryid', 'idp_type', 'provision_status', 'user_username', 'user_lastname', 'host', 'port', 'base_dn', 'bind_dn', 'search_attribute', 'start_tls', 'idp_entityid', 'sso_url', 'slo_url', 'username_attribute', 'sp_entityid', 'nameid_format', 'sign_messages', 'sign_assertions', 'sign_authn_requests', 'sign_logout_requests', 'sign_logout_responses', 'encrypt_nameid', 'encrypt_assertions', 'search_filter', 'group_basedn', 'group_name', 'group_member', 'user_ref_attr', 'group_filter', 'group_membership' ], 'userdirectoryids' => [$userdirectoryid], 'selectProvisionMedia' => ['name', 'mediatypeid', 'attribute'], 'selectProvisionGroups' => ['name', 'roleid', 'user_groups'] ]); $userdirectory = reset($userdirectories); if (!$userdirectory || $userdirectory['provision_status'] == JIT_PROVISIONING_DISABLED) { return new self($userdirectory, []); } if ($userdirectory['idp_type'] == IDP_TYPE_LDAP) { $userdirectory += DB::select('userdirectory_ldap', [ 'output' => ['bind_password'], 'filter' => ['userdirectoryid' => $userdirectoryid] ])[0]; } $mapping_roles = []; if ($userdirectory['provision_groups']) { $mapping_roles = DB::select('role', [ 'output' => ['roleid', 'name', 'type'], 'roleids' => array_column($userdirectory['provision_groups'], 'roleid', 'roleid'), 'preservekeys' => true ]); } return new self($userdirectory, $mapping_roles); } /** * Get configuration options for idp. * * @return array */ public function getIdpConfig(): array { $keys = [ IDP_TYPE_LDAP => ['host', 'port', 'base_dn', 'bind_dn', 'search_attribute', 'start_tls', 'search_filter', 'group_basedn', 'group_name', 'group_member', 'user_ref_attr', 'group_filter', 'group_membership', 'user_username', 'user_lastname', 'bind_password' ], IDP_TYPE_SAML => ['idp_entityid', 'sso_url', 'slo_url', 'username_attribute', 'sp_entityid', 'nameid_format', 'sign_messages', 'sign_assertions', 'sign_authn_requests', 'sign_logout_requests', 'sign_logout_responses', 'encrypt_nameid', 'encrypt_assertions', 'group_name', 'user_username', 'user_lastname', 'scim_status' ] ]; return array_key_exists($this->userdirectory['idp_type'], $keys) ? array_intersect_key($this->userdirectory, array_flip($keys[$this->userdirectory['idp_type']])) : []; } /** * Get provisioning status. * * @return bool Return true when enabled. */ public function isProvisioningEnabled(): bool { return $this->userdirectory['provision_status'] == JIT_PROVISIONING_ENABLED; } /** * Get array of attributes to request from external source when requesting user data. * * @return array Array of attributes names to request from external source. */ public function getUserIdpAttributes(): array { $fields = $this->userdirectory['idp_type'] == IDP_TYPE_LDAP ? ['user_username', 'user_lastname', 'search_attribute', 'group_membership', 'user_ref_attr'] : ['user_username', 'user_lastname']; $attributes = array_intersect_key($this->userdirectory, array_flip($fields)); $attributes = array_merge($this->getUserIdpMediaAttributes(), $attributes); return array_keys(array_flip(array_filter($attributes, 'strlen'))); } /** * Get array of attributes to request from external source when requesting user group data. * * @return array */ public function getGroupIdpAttributes(): array { $attributes = [$this->userdirectory['group_name']]; return array_values(array_filter($attributes, 'strlen')); } /** * Get array of media attributes to request from external source. * * @return array */ public function getUserIdpMediaAttributes(): array { $attributes = array_column($this->userdirectory['provision_media'], 'attribute'); return array_keys(array_flip(array_filter($attributes, 'strlen'))); } /** * Return array with user fields created from matched provision data on external user data attributes. * * @param array $idp_user User data from external source, LDAP/SAML. * @param bool $case_sensitive How IdP attributes should be matched. * * @return array */ public function getUserAttributes(array $idp_user, bool $case_sensitive = true): array { $user_idp_fields = array_filter([ 'name' => $this->userdirectory['user_username'], 'surname' => $this->userdirectory['user_lastname'] ], 'strlen'); $user = []; $idp_user_lowercased = []; if (!$case_sensitive) { foreach ($idp_user as $idp_attr => $idp_value) { $idp_user_lowercased[strtolower($idp_attr)] = $idp_value; } } foreach ($user_idp_fields as $user_field => $idp_field) { if (array_key_exists($idp_field, $idp_user)) { $user[$user_field] = $idp_user[$idp_field]; continue; } $idp_field = strtolower($idp_field); if (array_key_exists($idp_field, $idp_user_lowercased)) { $user[$user_field] = $idp_user_lowercased[$idp_field]; } } return $user; } /** * Return array with user media created from matched provision_media on external user data attributes. * * @param array $idp_user User data from external source, LDAP/SAML. * @param bool $case_sensitive How IdP attributes should be matched. * * @return array * []['mediatypeid'] * []['sendto'] */ public function getUserMedias(array $idp_user, bool $case_sensitive = true): array { $user_medias = []; $idp_user_lowercased = []; if (!$case_sensitive) { foreach ($idp_user as $idp_attr => $idp_value) { $idp_user_lowercased[strtolower($idp_attr)] = $idp_value; } } foreach ($this->userdirectory['provision_media'] as $idp_attributes) { $idp_field = $idp_attributes['attribute']; if (array_key_exists($idp_field, $idp_user)) { $user_medias[] = [ 'name' => $idp_attributes['name'], 'mediatypeid' => $idp_attributes['mediatypeid'], 'sendto' => [$idp_user[$idp_field]] ]; continue; } $idp_field = strtolower($idp_field); if (array_key_exists($idp_field, $idp_user_lowercased)) { $user_medias[] = [ 'name' => $idp_attributes['name'], 'mediatypeid' => $idp_attributes['mediatypeid'], 'sendto' => [$idp_user_lowercased[$idp_field]] ]; } } return $user_medias; } /** * Return array with user groups matched to provision groups criteria and roleid. * * @param array $group_names User group names data from external source, LDAP/SAML. * * @return array * ['roleid'] Matched roleid to set for user. Is 0 when no match. * ['usrgrps'] Matched mapping user groups to set for user. Empty array when no match. * ['usrgrps'][]['usrgrpid'] User group */ public function getUserGroupsAndRole(array $group_names): array { $user = ['usrgrps' => [], 'roleid' => 0]; if (!$this->userdirectory['provision_groups']) { return $user; } $roleids = []; $user_groups = []; foreach ($this->userdirectory['provision_groups'] as $provision_group) { $match = ($provision_group['name'] === '*'); if (strpos($provision_group['name'], '*') === false) { $match = in_array($provision_group['name'], $group_names); } elseif (!$match) { $regex = preg_quote($provision_group['name'], '/'); $regex = '/'.str_replace('\\*', '.*', $regex).'/'; $match = false; foreach ($group_names as $group_name) { $match = $match || preg_match($regex, $group_name); } } if ($match) { $roleids[$provision_group['roleid']] = true; $user_groups = array_merge($user_groups, $provision_group['user_groups']); } } if (!$user_groups) { return $user; } $user['usrgrps'] = array_values(array_column($user_groups, null, 'usrgrpid')); $roles = array_intersect_key($this->mapping_roles, $roleids); if ($roles) { CArrayHelper::sort($roles, [ ['field' => 'type', 'order' => ZBX_SORT_DOWN], ['field' => 'name', 'order' => ZBX_SORT_UP] ]); ['roleid' => $user['roleid']] = reset($roles); } return $user; } }