ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilOpenIdConnectUserSync.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
25 {
26  public const AUTH_MODE = 'oidc';
27 
29  private ilLogger $logger;
32  private string $ext_account = '';
33  private string $int_account = '';
34  private int $usr_id = 0;
35 
36  public function __construct(ilOpenIdConnectSettings $settings, stdClass $user_info)
37  {
38  global $DIC;
39 
40  $this->settings = $settings;
41  $this->user_info = $user_info;
42 
43  $this->logger = $DIC->logger()->auth();
44  $this->writer = new ilXmlWriter();
45  }
46 
47  public function setExternalAccount(string $ext_account): void
48  {
49  $this->ext_account = $ext_account;
50  }
51 
52  public function setInternalAccount(string $int_account): void
53  {
54  $this->int_account = $int_account;
55  $this->usr_id = (int) ilObjUser::_lookupId($this->int_account);
56  }
57 
58  public function getUserId(): int
59  {
60  return $this->usr_id;
61  }
62 
63  public function needsCreation(): bool
64  {
65  $this->logger->dump($this->int_account, ilLogLevel::DEBUG);
66 
67  return $this->int_account === '';
68  }
69 
73  public function updateUser(): bool
74  {
75  if ($this->needsCreation() && !$this->settings->isSyncAllowed()) {
76  throw new ilOpenIdConnectSyncForbiddenException('No internal account given.');
77  }
78 
79  $this->transformToXml();
80 
81  $importParser = new ilUserImportParser();
82  $importParser->setXMLContent($this->writer->xmlDumpMem(false));
83 
84  $roles = $this->parseRoleAssignments();
85  $importParser->setRoleAssignment($roles);
86 
87  $importParser->setFolderId(USER_FOLDER_ID);
88  $importParser->startParsing();
89  $debug = $importParser->getProtocol();
90 
92  self::AUTH_MODE,
93  $this->ext_account
94  );
95  $this->setInternalAccount($int_account);
96 
97  return true;
98  }
99 
100  protected function transformToXml(): void
101  {
102  $this->writer->xmlStartTag('Users');
103 
104  if ($this->needsCreation()) {
105  $this->writer->xmlStartTag('User', ['Action' => 'Insert']);
106  $this->writer->xmlElement('Login', [], ilAuthUtils::_generateLogin($this->ext_account));
107  } else {
108  $this->writer->xmlStartTag(
109  'User',
110  [
111  'Id' => $this->getUserId(),
112  'Action' => 'Update'
113  ]
114  );
115  $this->writer->xmlElement('Login', [], $this->int_account);
116  }
117 
118  $this->writer->xmlElement('ExternalAccount', array(), $this->ext_account);
119  $this->writer->xmlElement('AuthMode', array('type' => self::AUTH_MODE), null);
120 
121  $this->parseRoleAssignments();
122 
123  if ($this->needsCreation()) {
124  $this->writer->xmlElement('Active', array(), "true");
125  $this->writer->xmlElement('TimeLimitOwner', array(), 7);
126  $this->writer->xmlElement('TimeLimitUnlimited', array(), 1);
127  $this->writer->xmlElement('TimeLimitFrom', array(), time());
128  $this->writer->xmlElement('TimeLimitUntil', array(), time());
129  }
130 
131  foreach ($this->settings->getProfileMappingFields() as $field => $lng_key) {
132  $connect_name = $this->settings->getProfileMappingFieldValue($field);
133  if (!$connect_name) {
134  $this->logger->debug('Ignoring unconfigured field: ' . $field);
135  continue;
136  }
137  if (!$this->needsCreation() && !$this->settings->getProfileMappingFieldUpdate($field)) {
138  $this->logger->debug('Ignoring ' . $field . ' for update.');
139  continue;
140  }
141 
142  $value = $this->valueFrom($connect_name);
143  if ($value === '') {
144  $this->logger->debug('Cannot find user data in ' . $connect_name);
145  continue;
146  }
147 
148  switch ($field) {
149  case 'firstname':
150  $this->writer->xmlElement('Firstname', [], $value);
151  break;
152 
153  case 'lastname':
154  $this->writer->xmlElement('Lastname', [], $value);
155  break;
156 
157  case 'email':
158  $this->writer->xmlElement('Email', [], $value);
159  break;
160 
161  case 'birthday':
162  $this->writer->xmlElement('Birthday', [], $value);
163  break;
164  }
165  }
166  $this->writer->xmlEndTag('User');
167  $this->writer->xmlEndTag('Users');
168 
169  $this->logger->debug($this->writer->xmlDumpMem());
170  }
171 
176  protected function parseRoleAssignments(): array
177  {
178  $this->logger->debug('Parsing role assignments');
179 
180  $found_role = false;
181 
182  $roles_assignable[$this->settings->getRole()] = $this->settings->getRole();
183 
184  $this->logger->dump($this->settings->getRoleMappings(), ilLogLevel::DEBUG);
185 
186  foreach ($this->settings->getRoleMappings() as $role_id => $role_info) {
187  $this->logger->dump($role_id, ilLogLevel::DEBUG);
188  $this->logger->dump($role_info, ilLogLevel::DEBUG);
189 
190  if ($role_info['value'] === '') {
191  $this->logger->debug('No role mapping for role: ' . $role_id);
192  continue;
193  }
194 
195  [$role_attribute, $role_value] = array_map(trim(...), explode('::', $role_info['value']));
196 
197  if (!$role_attribute || !$role_value) {
198  $this->logger->debug('No valid role mapping configuration for: ' . $role_id);
199  continue;
200  }
201 
202  if (!isset($this->user_info->{$role_attribute})) {
203  $this->logger->debug('No user info passed');
204  continue;
205  }
206 
207  if (!$role_info['update'] && !$this->needsCreation()) {
208  $this->logger->debug('No user role update for role: ' . $role_id);
209  continue;
210  }
211 
212  if (is_array($this->user_info->{$role_attribute})) {
213  $roles_claim = array_map(
214  static function (mixed $value): string {
215  return match (true) {
216  is_string($value) => trim($value),
217  $value instanceof stdClass && property_exists($value, 'id') => trim((string) $value->id),
218  $value instanceof stdClass && property_exists($value, 'value') => trim((string) $value->value),
219  default => throw new DomainException(sprintf(
220  'Unexpected role value type, please check your provider configuration: %s',
221  print_r($value, true)
222  )),
223  };
224  },
225  $this->user_info->{$role_attribute}
226  );
227  if (!in_array($role_value, $roles_claim, true)) {
228  $this->logger->debug('User account has no ' . $role_value);
229  continue;
230  }
231  } elseif (strcmp(trim($this->user_info->{$role_attribute}), $role_value) !== 0) {
232  $this->logger->debug('User account has no ' . $role_value);
233  continue;
234  }
235 
236  $this->logger->debug('Matching role mapping for role_id: ' . $role_id);
237 
238  $found_role = true;
239  $roles_assignable[(int) $role_id] = (int) $role_id;
240  $long_role_id = ('il_' . IL_INST_ID . '_role_' . $role_id);
241 
242  $this->writer->xmlElement(
243  'Role',
244  [
245  'Id' => $long_role_id,
246  'Type' => 'Global',
247  'Action' => 'Assign'
248  ],
249  null
250  );
251  }
252 
253  if (!$found_role && $this->needsCreation()) {
254  $long_role_id = ('il_' . IL_INST_ID . '_role_' . $this->settings->getRole());
255 
256  // add default role
257  $this->writer->xmlElement(
258  'Role',
259  [
260  'Id' => $long_role_id,
261  'Type' => 'Global',
262  'Action' => 'Assign'
263  ],
264  null
265  );
266  }
267 
268  return $roles_assignable;
269  }
270 
271  protected function valueFrom(string $connect_name): string
272  {
273  if (!$connect_name) {
274  return '';
275  }
276  if (!property_exists($this->user_info, $connect_name)) {
277  $this->logger->debug('Cannot find property ' . $connect_name . ' in user info ');
278  return '';
279  }
280 
281  return (string) $this->user_info->{$connect_name};
282  }
283 }
static _generateLogin(string $a_login)
generate free login by starting with a default string and adding postfix numbers
const IL_INST_ID
Definition: constants.php:40
const USER_FOLDER_ID
Definition: constants.php:33
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _lookupId($a_user_str)
static _checkExternalAuthAccount(string $a_auth, string $a_account, bool $tryFallback=true)
check whether external account and authentication method matches with a user
global $DIC
Definition: feed.php:28
parseRoleAssignments()
Parse role assignments.
__construct(ilOpenIdConnectSettings $settings, stdClass $user_info)