Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20dae3caa9 | ||
|
|
7d78a1f515 | ||
|
|
8290e347f5 |
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 80 KiB |
@@ -62,6 +62,15 @@ plus six digits. It searches `BaseDn` by `sAMAccountName`, creates the user when
|
||||
absent, moves it to the mapped OU when required, sets `userPrincipalName`, and
|
||||
passes the submitted password directly to ADSI `SetPassword`.
|
||||
|
||||
Before an account becomes usable, the broker applies explicit deny ACEs for the
|
||||
Active Directory `Change Password` extended right to SELF and Everyone. Users
|
||||
beneath `OU=Usuarios-SGU` therefore cannot replace the synchronized password
|
||||
from Windows, Ctrl+Alt+Delete, LDAP or another client. The broker's
|
||||
administrative `SetPassword` operation uses the separate `Reset Password` right
|
||||
and remains able to synchronize the current institutional credential after each
|
||||
successful SGU authentication. Repeated synchronizations detect the existing
|
||||
ACEs and do not duplicate them.
|
||||
|
||||
When the authenticated HTML exposes recognized stable IDs, the broker also
|
||||
updates the applicable `displayName`, `givenName`, `sn`, `mail`, `title`,
|
||||
`department`, `employeeType`, `employeeID`, `streetAddress`, `l`, `st`, and
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# SGU Credential Provider 0.6.7
|
||||
|
||||
Esta versión separa la imagen de la pantalla de bloqueo de la base utilizada
|
||||
para generar el escritorio personalizado.
|
||||
|
||||
- `darkblue-lockscreen.jpg` contiene únicamente el fondo azul institucional,
|
||||
sin logotipo ni datos personales.
|
||||
- `darkblue.jpg` continúa como base del escritorio de cada sesión y conserva el
|
||||
logotipo oficial integrado.
|
||||
- El logo de escritorio se reduce nuevamente y se desplaza ligeramente hacia
|
||||
arriba, sin interferir con el saludo central.
|
||||
- El instalador, la GPO clásica, `PersonalizationCSP` y los paquetes de servidor
|
||||
y cliente utilizan explícitamente el archivo correspondiente a cada función.
|
||||
- Linux continúa usando sólo la base de escritorio personalizada.
|
||||
|
||||
No se modifican la autenticación, los perfiles locales ni las reglas de
|
||||
enrolamiento.
|
||||
@@ -0,0 +1,17 @@
|
||||
# SGU Credential Provider 0.6.8
|
||||
|
||||
Esta versión declara al Auth Broker como la única autoridad de contraseñas para
|
||||
todas las cuentas administradas bajo `OU=Usuarios-SGU`.
|
||||
|
||||
- Cada creación, movimiento o actualización de una cuenta agrega de forma
|
||||
idempotente las denegaciones SELF y Everyone para el derecho extendido de
|
||||
Active Directory `Change Password`.
|
||||
- El usuario no puede cambiar la contraseña desde Windows, Ctrl+Alt+Delete,
|
||||
LDAP ni herramientas equivalentes.
|
||||
- El broker conserva el derecho administrativo separado `Reset Password` y
|
||||
continúa sincronizando la contraseña institucional exacta mediante
|
||||
`SetPassword` después de una autenticación SGU válida.
|
||||
- El despliegue del broker recorre todas las cuentas existentes en
|
||||
`Usuarios-SGU` y corrige aquellas que todavía permiten cambios directos.
|
||||
|
||||
No se modifica la contraseña institucional ni se almacena una copia adicional.
|
||||
@@ -0,0 +1,16 @@
|
||||
# SGU Credential Provider 0.6.9
|
||||
|
||||
Esta versión completa la autoridad de contraseñas del Auth Broker en la
|
||||
interfaz de Windows.
|
||||
|
||||
- La GPO `SGU - User session restrictions` habilita la directiva **Remove
|
||||
Change Password** para todas las cuentas bajo `OU=Usuarios-SGU`.
|
||||
- La opción **Cambiar una contraseña** deja de aparecer en la pantalla de
|
||||
seguridad de Ctrl+Alt+Supr.
|
||||
- La protección real continúa en Active Directory mediante las denegaciones
|
||||
del derecho extendido `Change Password`; la GPO únicamente evita mostrar una
|
||||
acción que esas cuentas no pueden completar.
|
||||
|
||||
El Auth Broker conserva el derecho administrativo separado `Reset Password`
|
||||
para sincronizar la contraseña institucional después de una autenticación SGU
|
||||
válida.
|
||||
@@ -18,6 +18,13 @@ the same source list. RDP uses a separate allowlist. See
|
||||
`Marshal.ZeroFreeGlobalAllocUnicode`; managed references are released as soon
|
||||
as each request completes.
|
||||
- The broker uses the exact received value for both NTLM and AD `SetPassword`.
|
||||
- Managed `Usuarios-SGU` accounts deny the SELF and Everyone `Change Password`
|
||||
extended right. Only an administrator or the broker through the separate
|
||||
`Reset Password` right can replace the AD password.
|
||||
- The `SGU - User session restrictions` GPO also hides the **Change a
|
||||
password** command from the Windows Ctrl+Alt+Delete security screen for
|
||||
managed users. This is a user-interface complement to the directory ACL,
|
||||
not a substitute for it.
|
||||
- There is no HMAC password, pepper, local password cache, Supabase password, or
|
||||
other derived credential in this Windows path.
|
||||
- Neither application logs request bodies or passwords. Deployment configuration
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# Fondo de bienvenida personalizado
|
||||
|
||||
El enrolamiento instala un fondo base azul que ya contiene el logotipo blanco
|
||||
oficial de Universidad La Salle México en la esquina inferior derecha, con un
|
||||
margen interior que lo mantiene separado de los bordes, las familias `Indivisa Text Sans` y
|
||||
`Indivisa Text Serif`, y un generador local. El logotipo está horneado en
|
||||
`assets/branding/darkblue.jpg`: el generador no carga, redimensiona ni compone
|
||||
otro logo durante el inicio de sesión. La GPO de equipos
|
||||
El enrolamiento instala dos fondos base azules. El escritorio personalizado usa
|
||||
`assets/branding/darkblue.jpg`, que contiene el logotipo blanco oficial de
|
||||
Universidad La Salle México, pequeño y elevado en el sector inferior derecho.
|
||||
La pantalla de bloqueo usa `assets/branding/darkblue-lockscreen.jpg`, que sólo
|
||||
contiene el fondo azul y nunca muestra el logo. También instala las familias
|
||||
`Indivisa Text Sans` y `Indivisa Text Serif`, y un generador local. El logotipo
|
||||
está horneado únicamente en la base de escritorio: el generador no carga,
|
||||
redimensiona ni compone otro logo durante el inicio de sesión. La GPO de equipos
|
||||
`SGU - Windows client experience` ejecuta el generador al abrir cada sesión y
|
||||
mantiene el fondo base en la pantalla de bloqueo.
|
||||
mantiene la base limpia independiente en la pantalla de bloqueo.
|
||||
|
||||
Windows no conoce todavía la identidad que se autenticará mientras muestra la
|
||||
pantalla previa al inicio de sesión. Por ello, esa pantalla utiliza el fondo base
|
||||
|
||||
@@ -201,6 +201,19 @@ if ($RemoteDesktopGroupDn) {
|
||||
}
|
||||
}
|
||||
|
||||
$managedUsersPasswordChangeCorrected = 0
|
||||
if ($PSCmdlet.ShouldProcess($usersOuDn, 'Deny direct password changes for every managed SGU user')) {
|
||||
$managedUsers = @(Get-ADUser -Filter * -SearchBase $usersOuDn -SearchScope Subtree `
|
||||
-Properties CannotChangePassword -Server $LdapHost -ErrorAction Stop)
|
||||
foreach ($managedUser in $managedUsers) {
|
||||
if (-not $managedUser.CannotChangePassword) {
|
||||
Set-ADAccountControl -Identity $managedUser.DistinguishedName `
|
||||
-CannotChangePassword $true -Server $LdapHost -Confirm:$false
|
||||
$managedUsersPasswordChangeCorrected++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($file in @('SGU.AuthBroker.exe', 'SGU.AuthBroker.dll', 'appsettings.json')) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $PublishPath $file))) {
|
||||
throw "PublishPath is missing $file."
|
||||
@@ -356,4 +369,5 @@ if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker
|
||||
}
|
||||
|
||||
Get-Service -Name $serviceName | Select-Object Name, Status, StartType,
|
||||
@{ Name = 'EventLog'; Expression = { $brokerEventLogName } }
|
||||
@{ Name = 'EventLog'; Expression = { $brokerEventLogName } },
|
||||
@{ Name = 'ExistingUsersPasswordChangeDenied'; Expression = { $managedUsersPasswordChangeCorrected } }
|
||||
|
||||
@@ -38,6 +38,7 @@ $interactiveLogonPolicyPath = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\P
|
||||
$accountPictureSourcePath = Join-Path $PublishPath 'branding\user.png'
|
||||
$accountPictureDirectory = Join-Path $env:ProgramData 'Microsoft\User Account Pictures'
|
||||
$welcomeWallpaperSourcePath = Join-Path $PublishPath 'branding\darkblue.jpg'
|
||||
$welcomeLockScreenSourcePath = Join-Path $PublishPath 'branding\darkblue-lockscreen.jpg'
|
||||
$welcomeWallpaperScriptSourcePath = Join-Path $PublishPath 'branding\Set-SguWelcomeWallpaper.ps1'
|
||||
$welcomeFontsSourcePath = Join-Path $PublishPath 'branding\fonts'
|
||||
$welcomeWallpaperDirectory = Join-Path $env:ProgramData 'SGU\Branding'
|
||||
@@ -128,6 +129,7 @@ function Install-DefaultAccountPicture {
|
||||
|
||||
function Install-WelcomeWallpaperAssets {
|
||||
if (-not (Test-Path -LiteralPath $welcomeWallpaperSourcePath -PathType Leaf) -or
|
||||
-not (Test-Path -LiteralPath $welcomeLockScreenSourcePath -PathType Leaf) -or
|
||||
-not (Test-Path -LiteralPath $welcomeWallpaperScriptSourcePath -PathType Leaf)) {
|
||||
return $false
|
||||
}
|
||||
@@ -135,6 +137,8 @@ function Install-WelcomeWallpaperAssets {
|
||||
New-Item -ItemType Directory -Path $welcomeWallpaperDirectory -Force | Out-Null
|
||||
Copy-Item -LiteralPath $welcomeWallpaperSourcePath `
|
||||
-Destination (Join-Path $welcomeWallpaperDirectory 'darkblue.jpg') -Force
|
||||
Copy-Item -LiteralPath $welcomeLockScreenSourcePath `
|
||||
-Destination (Join-Path $welcomeWallpaperDirectory 'darkblue-lockscreen.jpg') -Force
|
||||
Copy-Item -LiteralPath $welcomeWallpaperScriptSourcePath `
|
||||
-Destination (Join-Path $welcomeWallpaperDirectory 'Set-SguWelcomeWallpaper.ps1') -Force
|
||||
if (Test-Path -LiteralPath $welcomeFontsSourcePath -PathType Container) {
|
||||
@@ -358,5 +362,6 @@ catch {
|
||||
SystemPasswordProviderPreserved = $true
|
||||
WelcomeWallpaperAssetsInstalled =
|
||||
(Test-Path -LiteralPath (Join-Path $welcomeWallpaperDirectory 'darkblue.jpg') -PathType Leaf) -and
|
||||
(Test-Path -LiteralPath (Join-Path $welcomeWallpaperDirectory 'darkblue-lockscreen.jpg') -PathType Leaf) -and
|
||||
(Test-Path -LiteralPath (Join-Path $welcomeWallpaperDirectory 'Set-SguWelcomeWallpaper.ps1') -PathType Leaf)
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\lasalle-ma
|
||||
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\user.png')
|
||||
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue.jpg') `
|
||||
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\darkblue.jpg')
|
||||
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue-lockscreen.jpg') `
|
||||
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\darkblue-lockscreen.jpg')
|
||||
Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Set-SguWelcomeWallpaper.ps1') `
|
||||
-Destination (Join-Path $clientRoot 'payload\credential-provider\branding\Set-SguWelcomeWallpaper.ps1')
|
||||
foreach ($fontName in $welcomeFontNames) {
|
||||
@@ -238,6 +240,8 @@ Copy-RequiredFile -Source (Join-Path $PSScriptRoot 'Set-SguWelcomeWallpaper.ps1'
|
||||
-Destination (Join-Path $serverContentTarget 'welcome-wallpaper\Set-SguWelcomeWallpaper.ps1')
|
||||
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue.jpg') `
|
||||
-Destination (Join-Path $serverContentTarget 'welcome-wallpaper\darkblue.jpg')
|
||||
Copy-RequiredFile -Source (Join-Path $repositoryRoot 'assets\branding\darkblue-lockscreen.jpg') `
|
||||
-Destination (Join-Path $serverContentTarget 'welcome-wallpaper\darkblue-lockscreen.jpg')
|
||||
foreach ($fontName in $welcomeFontNames) {
|
||||
Copy-RequiredFile -Source (Join-Path $repositoryRoot "assets\branding\fonts\$fontName") `
|
||||
-Destination (Join-Path $serverContentTarget "welcome-wallpaper\fonts\$fontName")
|
||||
|
||||
@@ -5,7 +5,8 @@ param(
|
||||
[string]$DomainController = $env:COMPUTERNAME,
|
||||
[string]$EventCollectorFqdn,
|
||||
[string]$WelcomeWallpaperScriptPath = 'C:\ProgramData\SGU\Branding\Set-SguWelcomeWallpaper.ps1',
|
||||
[string]$WelcomeWallpaperBasePath = 'C:\ProgramData\SGU\Branding\darkblue.jpg'
|
||||
[string]$WelcomeWallpaperBasePath = 'C:\ProgramData\SGU\Branding\darkblue.jpg',
|
||||
[string]$WelcomeLockScreenBasePath = 'C:\ProgramData\SGU\Branding\darkblue-lockscreen.jpg'
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
@@ -113,14 +114,14 @@ $policies = @(
|
||||
# The machine GPO remains the authority for every interactive session. The
|
||||
# local payload lets the first desktop render without depending on SMB.
|
||||
@{ Key = $runPolicyKey; Name = 'SGUWelcomeWallpaper'; Type = 'String'; Value = $welcomeWallpaperCommand },
|
||||
@{ Key = $personalizationPolicyKey; Name = 'LockScreenImage'; Type = 'String'; Value = $WelcomeWallpaperBasePath },
|
||||
@{ Key = $personalizationPolicyKey; Name = 'LockScreenImage'; Type = 'String'; Value = $WelcomeLockScreenBasePath },
|
||||
@{ Key = $personalizationPolicyKey; Name = 'NoChangingLockScreen'; Type = 'DWord'; Value = 1 },
|
||||
|
||||
# Windows 11 Pro can ignore the legacy lock-screen policy even though it is
|
||||
# present in the registry. PersonalizationCSP provides the same local image
|
||||
# to Pro while remaining harmless on Enterprise and LTSC editions.
|
||||
@{ Key = $personalizationCspKey; Name = 'LockScreenImagePath'; Type = 'String'; Value = $WelcomeWallpaperBasePath },
|
||||
@{ Key = $personalizationCspKey; Name = 'LockScreenImageUrl'; Type = 'String'; Value = $WelcomeWallpaperBasePath },
|
||||
@{ Key = $personalizationCspKey; Name = 'LockScreenImagePath'; Type = 'String'; Value = $WelcomeLockScreenBasePath },
|
||||
@{ Key = $personalizationCspKey; Name = 'LockScreenImageUrl'; Type = 'String'; Value = $WelcomeLockScreenBasePath },
|
||||
@{ Key = $personalizationCspKey; Name = 'LockScreenImageStatus'; Type = 'DWord'; Value = 1 },
|
||||
@{ Key = $cloudContentPolicyKey; Name = 'DisableWindowsSpotlightFeatures'; Type = 'DWord'; Value = 1 },
|
||||
@{ Key = $cloudContentPolicyKey; Name = 'DisableWindowsSpotlightOnLockScreen'; Type = 'DWord'; Value = 1 }
|
||||
@@ -176,6 +177,6 @@ $linkEnabled = $link -and (
|
||||
PolicyCount = $configuredPolicies.Count
|
||||
EventCollector = $EventCollectorFqdn
|
||||
WelcomeWallpaperCommand = $welcomeWallpaperCommand
|
||||
LockScreenImage = $WelcomeWallpaperBasePath
|
||||
LockScreenImage = $WelcomeLockScreenBasePath
|
||||
Policies = [pscustomobject]$configuredPolicies
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ param(
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$policyKey = 'HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System'
|
||||
$policyValueName = 'DisableLockWorkstation'
|
||||
$disableChangePasswordValueName = 'DisableChangePassword'
|
||||
$desktopPolicyKey = 'HKCU\Software\Policies\Microsoft\Windows\Control Panel\Desktop'
|
||||
$themeKey = 'HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize'
|
||||
|
||||
@@ -76,6 +77,17 @@ if ($PSCmdlet.ShouldProcess($GpoName, 'Prevent SGU users from manually locking w
|
||||
-Type DWord `
|
||||
-Value 1 | Out-Null
|
||||
|
||||
# The directory ACL remains the security boundary. This user policy also
|
||||
# removes the unusable Change a password action from Ctrl+Alt+Delete.
|
||||
Set-GPRegistryValue `
|
||||
-Name $GpoName `
|
||||
-Domain $domainName `
|
||||
-Server $DomainController `
|
||||
-Key $policyKey `
|
||||
-ValueName $disableChangePasswordValueName `
|
||||
-Type DWord `
|
||||
-Value 1 | Out-Null
|
||||
|
||||
Set-GPRegistryValue `
|
||||
-Name $GpoName `
|
||||
-Domain $domainName `
|
||||
@@ -135,6 +147,12 @@ $configuredValue = Get-GPRegistryValue `
|
||||
-Server $DomainController `
|
||||
-Key $policyKey `
|
||||
-ValueName $policyValueName
|
||||
$disableChangePasswordValue = Get-GPRegistryValue `
|
||||
-Name $GpoName `
|
||||
-Domain $domainName `
|
||||
-Server $DomainController `
|
||||
-Key $policyKey `
|
||||
-ValueName $disableChangePasswordValueName
|
||||
$screenSaverValue = Get-GPRegistryValue `
|
||||
-Name $GpoName `
|
||||
-Domain $domainName `
|
||||
@@ -175,6 +193,7 @@ if ($WallpaperPath) {
|
||||
TargetOu = $TargetOuDn
|
||||
LinkEnabled = [bool]$linkEnabled
|
||||
DisableLockWorkstation = [int]$configuredValue.Value
|
||||
DisableChangePassword = [int]$disableChangePasswordValue.Value
|
||||
ScreenSaverDisabled = [string]$screenSaverValue.Value -eq '0'
|
||||
DarkMode = ([int]$appsThemeValue.Value -eq 0) -and ([int]$systemThemeValue.Value -eq 0)
|
||||
Wallpaper = $configuredWallpaper
|
||||
|
||||
@@ -22,4 +22,5 @@ internal static class BrokerEventIds
|
||||
internal static readonly EventId DirectoryOptionalMetadataFailure = new(1301, nameof(DirectoryOptionalMetadataFailure));
|
||||
internal static readonly EventId DirectoryGroupMembershipFailure = new(1302, nameof(DirectoryGroupMembershipFailure));
|
||||
internal static readonly EventId DirectoryRoleGroupMembershipAdded = new(1303, nameof(DirectoryRoleGroupMembershipAdded));
|
||||
internal static readonly EventId DirectoryPasswordChangeDenied = new(1304, nameof(DirectoryPasswordChangeDenied));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.DirectoryServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using SGU.AuthBroker.Core.Directory;
|
||||
using SGU.AuthBroker.Core.Identity;
|
||||
using SGU.AuthBroker.Core.Profiles;
|
||||
@@ -15,6 +17,12 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
private const int InfoAttributeMaximumLength = 1024;
|
||||
private const int AccountDisabled = 0x0002;
|
||||
private const int NormalAccount = 0x0200;
|
||||
private static readonly Guid ChangePasswordExtendedRight =
|
||||
new("AB721A53-1E2F-11D0-9819-00AA0040529B");
|
||||
private static readonly SecurityIdentifier EveryoneSid =
|
||||
new(WellKnownSidType.WorldSid, null);
|
||||
private static readonly SecurityIdentifier SelfSid =
|
||||
new(WellKnownSidType.SelfSid, null);
|
||||
private static readonly AuthenticationTypes BindFlags =
|
||||
AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing;
|
||||
|
||||
@@ -117,6 +125,12 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
// account without its required classification.
|
||||
EnsureRoleGroupMembership(user, identity);
|
||||
|
||||
// SGU remains the password authority. Deny the user's Change
|
||||
// Password extended right before making the account usable. This
|
||||
// does not deny the broker's administrative Reset Password right,
|
||||
// which ADSI SetPassword uses for each successful authentication.
|
||||
EnsureCannotChangePassword(user, identity.UserName);
|
||||
|
||||
// The exact institutional password received by the broker is passed to AD.
|
||||
// It is not derived, transformed, written to disk, or included in logs.
|
||||
user.Invoke("SetPassword", [password]);
|
||||
@@ -143,6 +157,52 @@ public sealed class ActiveDirectorySynchronizer(
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureCannotChangePassword(DirectoryEntry user, string institutionalUser)
|
||||
{
|
||||
user.Options!.SecurityMasks = SecurityMasks.Dacl;
|
||||
ActiveDirectorySecurity security = user.ObjectSecurity;
|
||||
if (!EnsureCannotChangePassword(security))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user.ObjectSecurity = security;
|
||||
user.CommitChanges();
|
||||
logger.LogInformation(
|
||||
BrokerEventIds.DirectoryPasswordChangeDenied,
|
||||
"Denied direct password changes for managed Active Directory user {InstitutionalUser}; SGU Auth Broker remains the password authority.",
|
||||
institutionalUser);
|
||||
}
|
||||
|
||||
internal static bool EnsureCannotChangePassword(ActiveDirectorySecurity security)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (SecurityIdentifier identity in new[] { EveryoneSid, SelfSid })
|
||||
{
|
||||
bool exists = security
|
||||
.GetAccessRules(includeExplicit: true, includeInherited: false, typeof(SecurityIdentifier))
|
||||
.OfType<ActiveDirectoryAccessRule>()
|
||||
.Any(rule =>
|
||||
rule.AccessControlType == AccessControlType.Deny &&
|
||||
rule.IdentityReference.Equals(identity) &&
|
||||
rule.ObjectType == ChangePasswordExtendedRight &&
|
||||
(rule.ActiveDirectoryRights & ActiveDirectoryRights.ExtendedRight) != 0);
|
||||
if (exists)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
security.AddAccessRule(new ActiveDirectoryAccessRule(
|
||||
identity,
|
||||
ActiveDirectoryRights.ExtendedRight,
|
||||
AccessControlType.Deny,
|
||||
ChangePasswordExtendedRight));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static void TryApplyProfile(
|
||||
DirectoryEntry user,
|
||||
UserIdentity identity,
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
||||
$synchronizerPath = Join-Path $repositoryRoot 'src\SGU.AuthBroker\Services\ActiveDirectorySynchronizer.cs'
|
||||
$deployPath = Join-Path $repositoryRoot 'scripts\Deploy-AuthBroker.ps1'
|
||||
$userPolicyPath = Join-Path $repositoryRoot 'scripts\Set-SguDomainUserPolicies.ps1'
|
||||
$synchronizer = Get-Content -LiteralPath $synchronizerPath -Raw
|
||||
$deploy = Get-Content -LiteralPath $deployPath -Raw
|
||||
$userPolicy = Get-Content -LiteralPath $userPolicyPath -Raw
|
||||
|
||||
Describe 'SGU Auth Broker password authority' {
|
||||
It 'denies the Change Password extended right to SELF and Everyone before SetPassword' {
|
||||
$synchronizer | Should Match 'AB721A53-1E2F-11D0-9819-00AA0040529B'
|
||||
$synchronizer | Should Match 'WellKnownSidType\.WorldSid'
|
||||
$synchronizer | Should Match 'WellKnownSidType\.SelfSid'
|
||||
$synchronizer | Should Match 'AccessControlType\.Deny'
|
||||
$synchronizer.IndexOf('EnsureCannotChangePassword(user', [StringComparison]::Ordinal) |
|
||||
Should BeLessThan $synchronizer.IndexOf('user.Invoke("SetPassword"', [StringComparison]::Ordinal)
|
||||
}
|
||||
|
||||
It 'repairs every existing account below Usuarios-SGU during broker deployment' {
|
||||
$deploy | Should Match 'Get-ADUser.*-SearchBase \$usersOuDn.*-SearchScope Subtree'
|
||||
$deploy | Should Match '(?s)Set-ADAccountControl.*-CannotChangePassword \$true'
|
||||
}
|
||||
|
||||
It 'removes Change Password from the Windows security screen for managed users' {
|
||||
$userPolicy | Should Match "disableChangePasswordValueName = 'DisableChangePassword'"
|
||||
$userPolicy | Should Match '(?s)-ValueName \$disableChangePasswordValueName.*-Type DWord.*-Value 1'
|
||||
}
|
||||
}
|
||||
@@ -83,4 +83,15 @@ Describe 'SGU Windows client enrollment scripts' {
|
||||
(Get-Content -LiteralPath $computerPolicyScriptPath -Raw) |
|
||||
Should Match "Name = 'HideFastUserSwitching'; Type = 'DWord'; Value = 1"
|
||||
}
|
||||
|
||||
It 'separates the clean lock screen base from the branded desktop base' {
|
||||
$installerSource = Get-Content -LiteralPath $credentialProviderInstallerPath -Raw
|
||||
$policySource = Get-Content -LiteralPath $computerPolicyScriptPath -Raw
|
||||
$packageSource = Get-Content -LiteralPath $packageScriptPath -Raw
|
||||
$installerSource | Should Match 'darkblue-lockscreen\.jpg'
|
||||
$packageSource | Should Match 'darkblue-lockscreen\.jpg'
|
||||
$policySource | Should Match "LockScreenImage.*WelcomeLockScreenBasePath"
|
||||
$policySource | Should Match "LockScreenImagePath.*WelcomeLockScreenBasePath"
|
||||
$policySource | Should Match "LockScreenImageUrl.*WelcomeLockScreenBasePath"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.DirectoryServices;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using SGU.AuthBroker.Core.Profiles;
|
||||
using SGU.AuthBroker.Services;
|
||||
using Xunit;
|
||||
@@ -31,4 +34,29 @@ public sealed class ActiveDirectorySynchronizerTests
|
||||
|
||||
Assert.Null(updated);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CannotChangePasswordRulesAreCompleteAndIdempotent()
|
||||
{
|
||||
ActiveDirectorySecurity security = new();
|
||||
|
||||
Assert.True(ActiveDirectorySynchronizer.EnsureCannotChangePassword(security));
|
||||
Assert.False(ActiveDirectorySynchronizer.EnsureCannotChangePassword(security));
|
||||
|
||||
Guid changePasswordRight = new("AB721A53-1E2F-11D0-9819-00AA0040529B");
|
||||
ActiveDirectoryAccessRule[] rules = security
|
||||
.GetAccessRules(includeExplicit: true, includeInherited: false, typeof(SecurityIdentifier))
|
||||
.OfType<ActiveDirectoryAccessRule>()
|
||||
.Where(rule =>
|
||||
rule.AccessControlType == AccessControlType.Deny &&
|
||||
rule.ObjectType == changePasswordRight &&
|
||||
(rule.ActiveDirectoryRights & ActiveDirectoryRights.ExtendedRight) != 0)
|
||||
.ToArray();
|
||||
|
||||
Assert.Equal(2, rules.Length);
|
||||
Assert.Contains(rules, rule => rule.IdentityReference.Equals(
|
||||
new SecurityIdentifier(WellKnownSidType.WorldSid, null)));
|
||||
Assert.Contains(rules, rule => rule.IdentityReference.Equals(
|
||||
new SecurityIdentifier(WellKnownSidType.SelfSid, null)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ Describe 'Welcome wallpaper with AD metadata' {
|
||||
}
|
||||
}
|
||||
$lowerRightWhitePixels = 0
|
||||
for ($x = 1225; $x -lt 1525; $x += 2) {
|
||||
for ($y = 805; $y -lt 930; $y += 2) {
|
||||
for ($x = 1260; $x -lt 1510; $x += 2) {
|
||||
for ($y = 740; $y -lt 860; $y += 2) {
|
||||
$pixel = $bitmap.GetPixel($x, $y)
|
||||
if ($pixel.R -gt 220 -and $pixel.G -gt 220 -and $pixel.B -gt 220) {
|
||||
$lowerRightWhitePixels++
|
||||
@@ -152,4 +152,23 @@ Describe 'Welcome wallpaper with AD metadata' {
|
||||
$bitmap.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
It 'keeps the lock screen base free of the desktop logo' {
|
||||
$bitmap = [Drawing.Bitmap]::FromFile((Join-Path $repositoryRoot 'assets\branding\darkblue-lockscreen.jpg'))
|
||||
try {
|
||||
$whitePixels = 0
|
||||
for ($x = 0; $x -lt $bitmap.Width; $x += 4) {
|
||||
for ($y = 0; $y -lt $bitmap.Height; $y += 4) {
|
||||
$pixel = $bitmap.GetPixel($x, $y)
|
||||
if ($pixel.R -gt 220 -and $pixel.G -gt 220 -and $pixel.B -gt 220) {
|
||||
$whitePixels++
|
||||
}
|
||||
}
|
||||
}
|
||||
($whitePixels -lt 10) | Should Be $true
|
||||
}
|
||||
finally {
|
||||
$bitmap.Dispose()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user