Compare commits

...
4 Commits
20 changed files with 300 additions and 15 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 80 KiB

+9
View File
@@ -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
+16
View File
@@ -0,0 +1,16 @@
# SGU Credential Provider 0.6.6
Esta versión ajusta la composición del fondo institucional introducido en
0.6.5.
- El logotipo blanco oficial de Universidad La Salle México queda integrado en
la esquina inferior derecha.
- Su tamaño se reduce ligeramente y se conserva un margen interior respecto de
ambos bordes para acercarlo visualmente al centro.
- El fondo original de `1600x1000`, el área central del saludo y la composición
dinámica de nombre y ubicación permanecen sin cambios.
- El generador sigue sin cargar ni componer un logotipo durante el inicio de
sesión.
No se modifican la autenticación, el enrolamiento, las políticas del dominio ni
el comportamiento de perfiles locales establecido en 0.6.4.
+17
View File
@@ -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.
+17
View File
@@ -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.
+16
View File
@@ -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.
+7
View File
@@ -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
+9 -6
View File
@@ -1,12 +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, 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
+15 -1
View File
@@ -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 } }
+5
View File
@@ -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)
}
+4
View File
@@ -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")
+6 -5
View File
@@ -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
}
+19
View File
@@ -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
+1
View File
@@ -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'
}
}
+11
View File
@@ -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)));
}
}
+32 -3
View File
@@ -123,20 +123,49 @@ Describe 'Welcome wallpaper with AD metadata' {
$result.LocationText | Should Be "Acceso al Aula Flexible`ndel Centro de Experiencia Digital."
}
It 'ships the logo inside the base image instead of compositing it at runtime' {
It 'ships the logo at the lower right of the base image instead of compositing it at runtime' {
$source | Should Not Match 'lasalle-logo-blanco|DrawImage.*logo|composite.*logo'
$bitmap = [Drawing.Bitmap]::FromFile((Join-Path $repositoryRoot 'assets\branding\darkblue.jpg'))
try {
$whitePixels = 0
$upperLeftWhitePixels = 0
for ($x = 60; $x -lt 390; $x += 2) {
for ($y = 45; $y -lt 175; $y += 2) {
$pixel = $bitmap.GetPixel($x, $y)
if ($pixel.R -gt 220 -and $pixel.G -gt 220 -and $pixel.B -gt 220) {
$upperLeftWhitePixels++
}
}
}
$lowerRightWhitePixels = 0
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++
}
}
}
($upperLeftWhitePixels -lt 25) | Should Be $true
($lowerRightWhitePixels -gt 250) | Should Be $true
}
finally {
$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 -gt 250) | Should Be $true
($whitePixels -lt 10) | Should Be $true
}
finally {
$bitmap.Dispose()