Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 1 | <?php |
| 2 | |
| 3 | namespace LdapRecord\Models\Attributes; |
| 4 | |
| 5 | use ReflectionClass; |
| 6 | |
| 7 | class AccountControl |
| 8 | { |
| 9 | const SCRIPT = 1; |
| 10 | |
| 11 | const ACCOUNTDISABLE = 2; |
| 12 | |
| 13 | const HOMEDIR_REQUIRED = 8; |
| 14 | |
| 15 | const LOCKOUT = 16; |
| 16 | |
| 17 | const PASSWD_NOTREQD = 32; |
| 18 | |
| 19 | const PASSWD_CANT_CHANGE = 64; |
| 20 | |
| 21 | const ENCRYPTED_TEXT_PWD_ALLOWED = 128; |
| 22 | |
| 23 | const TEMP_DUPLICATE_ACCOUNT = 256; |
| 24 | |
| 25 | const NORMAL_ACCOUNT = 512; |
| 26 | |
| 27 | const INTERDOMAIN_TRUST_ACCOUNT = 2048; |
| 28 | |
| 29 | const WORKSTATION_TRUST_ACCOUNT = 4096; |
| 30 | |
| 31 | const SERVER_TRUST_ACCOUNT = 8192; |
| 32 | |
| 33 | const DONT_EXPIRE_PASSWORD = 65536; |
| 34 | |
| 35 | const MNS_LOGON_ACCOUNT = 131072; |
| 36 | |
| 37 | const SMARTCARD_REQUIRED = 262144; |
| 38 | |
| 39 | const TRUSTED_FOR_DELEGATION = 524288; |
| 40 | |
| 41 | const NOT_DELEGATED = 1048576; |
| 42 | |
| 43 | const USE_DES_KEY_ONLY = 2097152; |
| 44 | |
| 45 | const DONT_REQ_PREAUTH = 4194304; |
| 46 | |
| 47 | const PASSWORD_EXPIRED = 8388608; |
| 48 | |
| 49 | const TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216; |
| 50 | |
| 51 | const PARTIAL_SECRETS_ACCOUNT = 67108864; |
| 52 | |
| 53 | /** |
| 54 | * The account control flag values. |
| 55 | * |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 56 | * @var array<int, int> |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 57 | */ |
| 58 | protected $values = []; |
| 59 | |
| 60 | /** |
| 61 | * Constructor. |
| 62 | * |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 63 | * @param ?int $flag |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 64 | */ |
| 65 | public function __construct($flag = null) |
| 66 | { |
| 67 | if (! is_null($flag)) { |
| 68 | $this->apply($flag); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Get the value when casted to string. |
| 74 | * |
| 75 | * @return string |
| 76 | */ |
| 77 | public function __toString() |
| 78 | { |
| 79 | return (string) $this->getValue(); |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Get the value when casted to int. |
| 84 | * |
| 85 | * @return int |
| 86 | */ |
| 87 | public function __toInt() |
| 88 | { |
| 89 | return $this->getValue(); |
| 90 | } |
| 91 | |
| 92 | /** |
| 93 | * Add the flag to the account control values. |
| 94 | * |
| 95 | * @param int $flag |
| 96 | * |
| 97 | * @return $this |
| 98 | */ |
| 99 | public function add($flag) |
| 100 | { |
| 101 | // Use the value as a key so if the same value |
| 102 | // is used, it will always be overwritten |
| 103 | $this->values[$flag] = $flag; |
| 104 | |
| 105 | return $this; |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Remove the flag from the account control. |
| 110 | * |
| 111 | * @param int $flag |
| 112 | * |
| 113 | * @return $this |
| 114 | */ |
| 115 | public function remove($flag) |
| 116 | { |
| 117 | unset($this->values[$flag]); |
| 118 | |
| 119 | return $this; |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Extract and apply the flag. |
| 124 | * |
| 125 | * @param int $flag |
| 126 | * |
| 127 | * @return void |
| 128 | */ |
| 129 | public function apply($flag) |
| 130 | { |
| 131 | $this->setValues($this->extractFlags($flag)); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Determine if the account control contains the given UAC flag(s). |
| 136 | * |
| 137 | * @param int $flag |
| 138 | * |
| 139 | * @return bool |
| 140 | */ |
| 141 | public function has($flag) |
| 142 | { |
| 143 | // Here we will extract the given flag into an array |
| 144 | // of possible flags. This will allow us to see if |
| 145 | // our AccountControl object contains any of them. |
| 146 | $flagsUsed = array_intersect( |
| 147 | $this->extractFlags($flag), |
| 148 | $this->values |
| 149 | ); |
| 150 | |
| 151 | return in_array($flag, $flagsUsed); |
| 152 | } |
| 153 | |
| 154 | /** |
| 155 | * Determine if the account control does not contain the given UAC flag(s). |
| 156 | * |
| 157 | * @param int $flag |
| 158 | * |
| 159 | * @return bool |
| 160 | */ |
| 161 | public function doesntHave($flag) |
| 162 | { |
| 163 | return ! $this->has($flag); |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Generate an LDAP filter based on the current value. |
| 168 | * |
| 169 | * @return string |
| 170 | */ |
| 171 | public function filter() |
| 172 | { |
| 173 | return sprintf('(UserAccountControl:1.2.840.113556.1.4.803:=%s)', $this->getValue()); |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * The logon script will be run. |
| 178 | * |
| 179 | * @return $this |
| 180 | */ |
| 181 | public function runLoginScript() |
| 182 | { |
| 183 | return $this->add(static::SCRIPT); |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * The user account is locked. |
| 188 | * |
| 189 | * @return $this |
| 190 | */ |
| 191 | public function accountIsLocked() |
| 192 | { |
| 193 | return $this->add(static::LOCKOUT); |
| 194 | } |
| 195 | |
| 196 | /** |
| 197 | * The user account is disabled. |
| 198 | * |
| 199 | * @return $this |
| 200 | */ |
| 201 | public function accountIsDisabled() |
| 202 | { |
| 203 | return $this->add(static::ACCOUNTDISABLE); |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * This is an account for users whose primary account is in another domain. |
| 208 | * |
| 209 | * This account provides user access to this domain, but not to any domain that |
| 210 | * trusts this domain. This is sometimes referred to as a local user account. |
| 211 | * |
| 212 | * @return $this |
| 213 | */ |
| 214 | public function accountIsTemporary() |
| 215 | { |
| 216 | return $this->add(static::TEMP_DUPLICATE_ACCOUNT); |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * This is a default account type that represents a typical user. |
| 221 | * |
| 222 | * @return $this |
| 223 | */ |
| 224 | public function accountIsNormal() |
| 225 | { |
| 226 | return $this->add(static::NORMAL_ACCOUNT); |
| 227 | } |
| 228 | |
| 229 | /** |
| 230 | * This is a permit to trust an account for a system domain that trusts other domains. |
| 231 | * |
| 232 | * @return $this |
| 233 | */ |
| 234 | public function accountIsForInterdomain() |
| 235 | { |
| 236 | return $this->add(static::INTERDOMAIN_TRUST_ACCOUNT); |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * This is a computer account for a computer that is running Microsoft |
| 241 | * Windows NT 4.0 Workstation, Microsoft Windows NT 4.0 Server, Microsoft |
| 242 | * Windows 2000 Professional, or Windows 2000 Server and is a member of this domain. |
| 243 | * |
| 244 | * @return $this |
| 245 | */ |
| 246 | public function accountIsForWorkstation() |
| 247 | { |
| 248 | return $this->add(static::WORKSTATION_TRUST_ACCOUNT); |
| 249 | } |
| 250 | |
| 251 | /** |
| 252 | * This is a computer account for a domain controller that is a member of this domain. |
| 253 | * |
| 254 | * @return $this |
| 255 | */ |
| 256 | public function accountIsForServer() |
| 257 | { |
| 258 | return $this->add(static::SERVER_TRUST_ACCOUNT); |
| 259 | } |
| 260 | |
| 261 | /** |
| 262 | * This is an MNS logon account. |
| 263 | * |
| 264 | * @return $this |
| 265 | */ |
| 266 | public function accountIsMnsLogon() |
| 267 | { |
| 268 | return $this->add(static::MNS_LOGON_ACCOUNT); |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * (Windows 2000/Windows Server 2003) This account does |
| 273 | * not require Kerberos pre-authentication for logging on. |
| 274 | * |
| 275 | * @return $this |
| 276 | */ |
| 277 | public function accountDoesNotRequirePreAuth() |
| 278 | { |
| 279 | return $this->add(static::DONT_REQ_PREAUTH); |
| 280 | } |
| 281 | |
| 282 | /** |
| 283 | * When this flag is set, it forces the user to log on by using a smart card. |
| 284 | * |
| 285 | * @return $this |
| 286 | */ |
| 287 | public function accountRequiresSmartCard() |
| 288 | { |
| 289 | return $this->add(static::SMARTCARD_REQUIRED); |
| 290 | } |
| 291 | |
| 292 | /** |
| 293 | * (Windows Server 2008/Windows Server 2008 R2) The account is a read-only domain controller (RODC). |
| 294 | * |
| 295 | * This is a security-sensitive setting. Removing this setting from an RODC compromises security on that server. |
| 296 | * |
| 297 | * @return $this |
| 298 | */ |
| 299 | public function accountIsReadOnly() |
| 300 | { |
| 301 | return $this->add(static::PARTIAL_SECRETS_ACCOUNT); |
| 302 | } |
| 303 | |
| 304 | /** |
| 305 | * The home folder is required. |
| 306 | * |
| 307 | * @return $this |
| 308 | */ |
| 309 | public function homeFolderIsRequired() |
| 310 | { |
| 311 | return $this->add(static::HOMEDIR_REQUIRED); |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * No password is required. |
| 316 | * |
| 317 | * @return $this |
| 318 | */ |
| 319 | public function passwordIsNotRequired() |
| 320 | { |
| 321 | return $this->add(static::PASSWD_NOTREQD); |
| 322 | } |
| 323 | |
| 324 | /** |
| 325 | * The user cannot change the password. This is a permission on the user's object. |
| 326 | * |
| 327 | * For information about how to programmatically set this permission, visit the following link: |
| 328 | * |
| 329 | * @see http://msdn2.microsoft.com/en-us/library/aa746398.aspx |
| 330 | * |
| 331 | * @return $this |
| 332 | */ |
| 333 | public function passwordCannotBeChanged() |
| 334 | { |
| 335 | return $this->add(static::PASSWD_CANT_CHANGE); |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Represents the password, which should never expire on the account. |
| 340 | * |
| 341 | * @return $this |
| 342 | */ |
| 343 | public function passwordDoesNotExpire() |
| 344 | { |
| 345 | return $this->add(static::DONT_EXPIRE_PASSWORD); |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * (Windows 2000/Windows Server 2003) The user's password has expired. |
| 350 | * |
| 351 | * @return $this |
| 352 | */ |
| 353 | public function passwordIsExpired() |
| 354 | { |
| 355 | return $this->add(static::PASSWORD_EXPIRED); |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * The user can send an encrypted password. |
| 360 | * |
| 361 | * @return $this |
| 362 | */ |
| 363 | public function allowEncryptedTextPassword() |
| 364 | { |
| 365 | return $this->add(static::ENCRYPTED_TEXT_PWD_ALLOWED); |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * When this flag is set, the service account (the user or computer account) |
| 370 | * under which a service runs is trusted for Kerberos delegation. |
| 371 | * |
| 372 | * Any such service can impersonate a client requesting the service. |
| 373 | * |
| 374 | * To enable a service for Kerberos delegation, you must set this |
| 375 | * flag on the userAccountControl property of the service account. |
| 376 | * |
| 377 | * @return $this |
| 378 | */ |
| 379 | public function trustForDelegation() |
| 380 | { |
| 381 | return $this->add(static::TRUSTED_FOR_DELEGATION); |
| 382 | } |
| 383 | |
| 384 | /** |
| 385 | * (Windows 2000/Windows Server 2003) The account is enabled for delegation. |
| 386 | * |
| 387 | * This is a security-sensitive setting. Accounts that have this option enabled |
| 388 | * should be tightly controlled. This setting lets a service that runs under the |
| 389 | * account assume a client's identity and authenticate as that user to other remote |
| 390 | * servers on the network. |
| 391 | * |
| 392 | * @return $this |
| 393 | */ |
| 394 | public function trustToAuthForDelegation() |
| 395 | { |
| 396 | return $this->add(static::TRUSTED_TO_AUTH_FOR_DELEGATION); |
| 397 | } |
| 398 | |
| 399 | /** |
| 400 | * When this flag is set, the security context of the user is not delegated to a |
| 401 | * service even if the service account is set as trusted for Kerberos delegation. |
| 402 | * |
| 403 | * @return $this |
| 404 | */ |
| 405 | public function doNotTrustForDelegation() |
| 406 | { |
| 407 | return $this->add(static::NOT_DELEGATED); |
| 408 | } |
| 409 | |
| 410 | /** |
| 411 | * (Windows 2000/Windows Server 2003) Restrict this principal to |
| 412 | * use only Data Encryption Standard (DES) encryption types for keys. |
| 413 | * |
| 414 | * @return $this |
| 415 | */ |
| 416 | public function useDesKeyOnly() |
| 417 | { |
| 418 | return $this->add(static::USE_DES_KEY_ONLY); |
| 419 | } |
| 420 | |
| 421 | /** |
| 422 | * Get the account control value. |
| 423 | * |
| 424 | * @return int |
| 425 | */ |
| 426 | public function getValue() |
| 427 | { |
| 428 | return array_sum($this->values); |
| 429 | } |
| 430 | |
| 431 | /** |
| 432 | * Get the account control flag values. |
| 433 | * |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 434 | * @return array<int, int> |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 435 | */ |
| 436 | public function getValues() |
| 437 | { |
| 438 | return $this->values; |
| 439 | } |
| 440 | |
| 441 | /** |
| 442 | * Set the account control values. |
| 443 | * |
Matthias Andreas Benkard | 1ba5381 | 2022-12-27 17:32:58 +0100 | [diff] [blame^] | 444 | * @param array<int, int> $flags |
Matthias Andreas Benkard | 7b2a3a1 | 2021-08-16 10:57:25 +0200 | [diff] [blame] | 445 | * |
| 446 | * @return void |
| 447 | */ |
| 448 | public function setValues(array $flags) |
| 449 | { |
| 450 | $this->values = $flags; |
| 451 | } |
| 452 | |
| 453 | /** |
| 454 | * Get all flags that are currently applied to the value. |
| 455 | * |
| 456 | * @return array |
| 457 | */ |
| 458 | public function getAppliedFlags() |
| 459 | { |
| 460 | $flags = $this->getAllFlags(); |
| 461 | |
| 462 | $exists = []; |
| 463 | |
| 464 | foreach ($flags as $name => $flag) { |
| 465 | if ($this->has($flag)) { |
| 466 | $exists[$name] = $flag; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | return $exists; |
| 471 | } |
| 472 | |
| 473 | /** |
| 474 | * Get all possible account control flags. |
| 475 | * |
| 476 | * @return array |
| 477 | */ |
| 478 | public function getAllFlags() |
| 479 | { |
| 480 | return (new ReflectionClass(__CLASS__))->getConstants(); |
| 481 | } |
| 482 | |
| 483 | /** |
| 484 | * Extracts the given flag into an array of flags used. |
| 485 | * |
| 486 | * @param int $flag |
| 487 | * |
| 488 | * @return array |
| 489 | */ |
| 490 | public function extractFlags($flag) |
| 491 | { |
| 492 | $flags = []; |
| 493 | |
| 494 | for ($i = 0; $i <= 26; $i++) { |
| 495 | if ((int) $flag & (1 << $i)) { |
| 496 | $flags[1 << $i] = 1 << $i; |
| 497 | } |
| 498 | } |
| 499 | |
| 500 | return $flags; |
| 501 | } |
| 502 | } |