Add SGU credential provider and authentication broker
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$PublishPath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ServerCertificateSubject,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^[0-9A-Fa-f ]{40,59}$')]
|
||||
[string[]]$AllowedClientThumbprints,
|
||||
|
||||
[string]$NtlmEndpoint = 'https://sgu.ulsa.edu.mx/',
|
||||
[string[]]$AllowedNtlmRedirectHosts = @('sgu.ulsa.edu.mx'),
|
||||
[string]$LdapHost = 'localhost',
|
||||
[string]$BaseDn = 'DC=lci,DC=lasalle,DC=mx',
|
||||
[string]$DomainNetbios = 'LCI',
|
||||
[string]$UpnSuffix = 'lci.lasalle.mx',
|
||||
[switch]$CreateMissingOus,
|
||||
[switch]$DisableCertificateRevocationCheckForLab
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$serviceName = 'SGUAuthBroker'
|
||||
$installPath = Join-Path $env:ProgramFiles 'SGU\AuthBroker'
|
||||
$normalizedClientThumbprints = @($AllowedClientThumbprints | ForEach-Object { $_ -replace ' ', '' })
|
||||
if ($normalizedClientThumbprints.Where({ $_.Length -ne 40 }).Count -gt 0) {
|
||||
throw 'Client certificate thumbprints must contain exactly 40 hexadecimal characters.'
|
||||
}
|
||||
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run this script from an elevated PowerShell session on the broker server.'
|
||||
}
|
||||
|
||||
$serverCertificate = Get-ChildItem Cert:\LocalMachine\My |
|
||||
Where-Object { $_.Subject -like "*$ServerCertificateSubject*" -and $_.HasPrivateKey } |
|
||||
Sort-Object NotAfter -Descending |
|
||||
Select-Object -First 1
|
||||
if (-not $serverCertificate) {
|
||||
throw 'The HTTPS server certificate with private key was not found in LocalMachine\My.'
|
||||
}
|
||||
if (-not $serverCertificate.Verify()) {
|
||||
throw 'The HTTPS server certificate chain is not trusted or is outside its validity period. Import the issuing CA chain; for a self-signed lab certificate, trust its public .cer in LocalMachine\Root.'
|
||||
}
|
||||
|
||||
if ($CreateMissingOus) {
|
||||
Import-Module ActiveDirectory -ErrorAction Stop
|
||||
$usersOuName = 'Usuarios-SGU'
|
||||
$usersOuDn = "OU=$usersOuName,$BaseDn"
|
||||
if (-not (Get-ADOrganizationalUnit -LDAPFilter "(ou=$usersOuName)" -SearchBase $BaseDn -SearchScope OneLevel -Server $LdapHost -ErrorAction SilentlyContinue)) {
|
||||
New-ADOrganizationalUnit -Name $usersOuName -Path $BaseDn -ProtectedFromAccidentalDeletion $true -Server $LdapHost | Out-Null
|
||||
}
|
||||
|
||||
foreach ($ouName in @('Docentes', 'Alumnos', 'Administrativos')) {
|
||||
$targetOu = Get-ADOrganizationalUnit -LDAPFilter "(ou=$ouName)" -SearchBase $usersOuDn -SearchScope OneLevel -Properties ProtectedFromAccidentalDeletion -Server $LdapHost -ErrorAction SilentlyContinue
|
||||
if ($targetOu) {
|
||||
if (-not $targetOu.ProtectedFromAccidentalDeletion) {
|
||||
$targetOuDn = [string]$targetOu.DistinguishedName
|
||||
Set-ADOrganizationalUnit -Identity $targetOuDn -ProtectedFromAccidentalDeletion $true -Server $LdapHost -Confirm:$false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
$legacyOu = Get-ADOrganizationalUnit -LDAPFilter "(ou=$ouName)" -SearchBase $BaseDn -SearchScope OneLevel -Properties ProtectedFromAccidentalDeletion -Server $LdapHost -ErrorAction SilentlyContinue
|
||||
if ($legacyOu) {
|
||||
$legacyOuDn = [string]$legacyOu.DistinguishedName
|
||||
try {
|
||||
if ($legacyOu.ProtectedFromAccidentalDeletion) {
|
||||
Set-ADOrganizationalUnit -Identity $legacyOuDn -ProtectedFromAccidentalDeletion $false -Server $LdapHost -Confirm:$false
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
Move-ADObject -Identity $legacyOuDn -TargetPath $usersOuDn -Server $LdapHost -Confirm:$false -ErrorAction Stop
|
||||
}
|
||||
finally {
|
||||
$currentOu = Get-ADOrganizationalUnit -LDAPFilter "(ou=$ouName)" -SearchBase $usersOuDn -SearchScope OneLevel -Properties ProtectedFromAccidentalDeletion -Server $LdapHost -ErrorAction SilentlyContinue
|
||||
if (-not $currentOu) {
|
||||
$currentOu = Get-ADOrganizationalUnit -LDAPFilter "(ou=$ouName)" -SearchBase $BaseDn -SearchScope OneLevel -Properties ProtectedFromAccidentalDeletion -Server $LdapHost -ErrorAction SilentlyContinue
|
||||
}
|
||||
if ($currentOu) {
|
||||
$currentOuDn = [string]$currentOu.DistinguishedName
|
||||
Set-ADOrganizationalUnit -Identity $currentOuDn -ProtectedFromAccidentalDeletion $true -Server $LdapHost -Confirm:$false
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
New-ADOrganizationalUnit -Name $ouName -Path $usersOuDn -ProtectedFromAccidentalDeletion $true -Server $LdapHost | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
}
|
||||
|
||||
$productionSettings = @{
|
||||
Kestrel = @{
|
||||
Endpoints = @{
|
||||
Https = @{
|
||||
Url = 'https://0.0.0.0:8443'
|
||||
Certificate = @{
|
||||
Subject = $ServerCertificateSubject
|
||||
Store = 'My'
|
||||
Location = 'LocalMachine'
|
||||
AllowInvalid = $false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Broker = @{
|
||||
Tls = @{
|
||||
AllowedClientThumbprints = $normalizedClientThumbprints
|
||||
CheckCertificateRevocation = -not $DisableCertificateRevocationCheckForLab
|
||||
}
|
||||
Ntlm = @{
|
||||
Endpoint = $NtlmEndpoint
|
||||
Domain = ''
|
||||
TimeoutSeconds = 15
|
||||
MaxRedirects = 5
|
||||
AllowedRedirectHosts = $AllowedNtlmRedirectHosts
|
||||
}
|
||||
Directory = @{
|
||||
LdapHost = $LdapHost
|
||||
BaseDn = $BaseDn
|
||||
DomainNetbios = $DomainNetbios
|
||||
UpnSuffix = $UpnSuffix
|
||||
ProfessorOuDn = "OU=Docentes,OU=Usuarios-SGU,$BaseDn"
|
||||
StudentOuDn = "OU=Alumnos,OU=Usuarios-SGU,$BaseDn"
|
||||
AdministrativeOuDn = "OU=Administrativos,OU=Usuarios-SGU,$BaseDn"
|
||||
CreateMissingOus = [bool]$CreateMissingOus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($installPath, 'Install the SGU Authentication Broker Windows service')) {
|
||||
if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) {
|
||||
Stop-Service -Name $serviceName -Force
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $installPath -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $PublishPath '*') -Destination $installPath -Recurse -Force
|
||||
$settingsJson = $productionSettings | ConvertTo-Json -Depth 8
|
||||
$utf8WithoutBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText(
|
||||
(Join-Path $installPath 'appsettings.Production.json'),
|
||||
$settingsJson,
|
||||
$utf8WithoutBom)
|
||||
|
||||
if (-not (Get-Service -Name $serviceName -ErrorAction SilentlyContinue)) {
|
||||
New-Service -Name $serviceName `
|
||||
-DisplayName 'SGU Authentication Broker' `
|
||||
-Description 'Validates SGU NTLM credentials and synchronizes Active Directory accounts.' `
|
||||
-BinaryPathName ('"{0}"' -f (Join-Path $installPath 'SGU.AuthBroker.exe')) `
|
||||
-StartupType Automatic
|
||||
}
|
||||
|
||||
if (-not (Get-NetFirewallRule -DisplayName 'SGU Authentication Broker (mTLS)' -ErrorAction SilentlyContinue)) {
|
||||
New-NetFirewallRule -DisplayName 'SGU Authentication Broker (mTLS)' `
|
||||
-Direction Inbound -Action Allow -Protocol TCP -LocalPort 8443 -Profile Domain | Out-Null
|
||||
}
|
||||
|
||||
Start-Service -Name $serviceName
|
||||
}
|
||||
|
||||
Get-Service -Name $serviceName | Select-Object Name, Status, StartType
|
||||
@@ -0,0 +1,15 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$CertificatePath
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run this script from an elevated PowerShell session.'
|
||||
}
|
||||
|
||||
$certificate = Import-Certificate -FilePath $CertificatePath -CertStoreLocation Cert:\LocalMachine\Root
|
||||
$certificate | Select-Object Subject, Thumbprint, NotAfter
|
||||
@@ -0,0 +1,160 @@
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string]$PublishPath,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^https://')]
|
||||
[string]$BrokerEndpoint,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^[0-9A-Fa-f ]{40,59}$')]
|
||||
[string]$ClientCertificateThumbprint,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^[0-9A-Fa-f ]{40,59}$')]
|
||||
[string]$ServerCertificateThumbprint,
|
||||
|
||||
[string]$DomainNetbios = 'LCI',
|
||||
|
||||
[ValidateRange(2, 30)]
|
||||
[int]$TimeoutSeconds = 6,
|
||||
|
||||
[switch]$InstallDotNetRuntime,
|
||||
|
||||
[string]$DotNetRuntimeInstallerPath
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
|
||||
$installPath = Join-Path $env:ProgramFiles 'SGU\CredentialProvider'
|
||||
$settingsPath = Join-Path $env:ProgramData 'SGU\CredentialProvider\settings.json'
|
||||
$providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$providerClassId"
|
||||
$classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId\InprocServer32"
|
||||
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run this script from an elevated PowerShell session.'
|
||||
}
|
||||
|
||||
function Test-DotNet10Runtime {
|
||||
$dotnetCandidates = @(
|
||||
(Get-Command dotnet -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source -ErrorAction SilentlyContinue),
|
||||
(Join-Path $env:ProgramFiles 'dotnet\dotnet.exe')
|
||||
) | Where-Object { $_ -and (Test-Path -LiteralPath $_ -PathType Leaf) } | Select-Object -Unique
|
||||
|
||||
foreach ($dotnet in $dotnetCandidates) {
|
||||
if (& $dotnet --list-runtimes | Select-String '^Microsoft\.NETCore\.App 10\.') {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
if (-not (Test-DotNet10Runtime)) {
|
||||
if (-not $InstallDotNetRuntime) {
|
||||
throw 'Microsoft .NET 10 x64 runtime is required. Re-run with -InstallDotNetRuntime or install it first.'
|
||||
}
|
||||
|
||||
if ($DotNetRuntimeInstallerPath) {
|
||||
if (-not (Test-Path -LiteralPath $DotNetRuntimeInstallerPath -PathType Leaf)) {
|
||||
throw 'DotNetRuntimeInstallerPath does not exist.'
|
||||
}
|
||||
|
||||
$runtimeInstaller = Start-Process -FilePath $DotNetRuntimeInstallerPath `
|
||||
-ArgumentList @('/install', '/quiet', '/norestart') -Wait -PassThru
|
||||
if ($runtimeInstaller.ExitCode -notin @(0, 1641, 3010)) {
|
||||
throw "The Microsoft .NET 10 runtime installer returned $($runtimeInstaller.ExitCode)."
|
||||
}
|
||||
}
|
||||
else {
|
||||
$winget = Get-Command winget -ErrorAction SilentlyContinue
|
||||
if (-not $winget) {
|
||||
throw 'winget is unavailable. Supply the offline installer with -DotNetRuntimeInstallerPath.'
|
||||
}
|
||||
|
||||
& $winget.Source install --id Microsoft.DotNet.Runtime.10 --exact --silent `
|
||||
--accept-package-agreements --accept-source-agreements --disable-interactivity
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw 'winget could not install the Microsoft .NET 10 runtime.'
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-DotNet10Runtime)) {
|
||||
throw 'The Microsoft .NET 10 runtime installation failed.'
|
||||
}
|
||||
}
|
||||
|
||||
$requiredFiles = @(
|
||||
'SGU.CredentialProvider.dll',
|
||||
'SGU.CredentialProvider.comhost.dll',
|
||||
'SGU.CredentialProvider.runtimeconfig.json',
|
||||
'SGU.CredentialProvider.deps.json',
|
||||
'Lithnet.CredentialProvider.dll',
|
||||
'SGU.AuthBroker.Core.dll'
|
||||
)
|
||||
foreach ($file in $requiredFiles) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $PublishPath $file))) {
|
||||
throw "PublishPath is missing $file."
|
||||
}
|
||||
}
|
||||
|
||||
$clientThumbprint = $ClientCertificateThumbprint -replace ' ', ''
|
||||
$serverThumbprint = $ServerCertificateThumbprint -replace ' ', ''
|
||||
if ($clientThumbprint.Length -ne 40 -or $serverThumbprint.Length -ne 40) {
|
||||
throw 'Certificate thumbprints must contain exactly 40 hexadecimal characters.'
|
||||
}
|
||||
$clientCertificate = Get-ChildItem Cert:\LocalMachine\My |
|
||||
Where-Object Thumbprint -eq $clientThumbprint |
|
||||
Select-Object -First 1
|
||||
if (-not $clientCertificate -or -not $clientCertificate.HasPrivateKey) {
|
||||
throw 'The client certificate with private key is not installed in LocalMachine\My.'
|
||||
}
|
||||
if (-not $clientCertificate.Verify()) {
|
||||
throw 'The client certificate chain is not trusted or is outside its validity period. Import the issuing CA chain; for a self-signed lab certificate, trust its public .cer in LocalMachine\Root.'
|
||||
}
|
||||
|
||||
$serverCertificate = Get-ChildItem Cert:\LocalMachine\Root, Cert:\LocalMachine\CA | Where-Object Thumbprint -eq $serverThumbprint
|
||||
if (-not $serverCertificate) {
|
||||
throw 'The broker server certificate or its issuing CA is not trusted by LocalMachine.'
|
||||
}
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($installPath, 'Install and register the SGU Credential Provider')) {
|
||||
New-Item -ItemType Directory -Path $installPath -Force | Out-Null
|
||||
Copy-Item -Path (Join-Path $PublishPath '*') -Destination $installPath -Recurse -Force
|
||||
|
||||
New-Item -ItemType Directory -Path (Split-Path $settingsPath -Parent) -Force | Out-Null
|
||||
$settingsJson = @{
|
||||
BrokerEndpoint = $BrokerEndpoint
|
||||
DomainNetbios = $DomainNetbios
|
||||
TimeoutSeconds = $TimeoutSeconds
|
||||
ClientCertificateThumbprint = $clientThumbprint
|
||||
ServerCertificateThumbprint = $serverThumbprint
|
||||
} | ConvertTo-Json
|
||||
$utf8WithoutBom = New-Object System.Text.UTF8Encoding($false)
|
||||
[System.IO.File]::WriteAllText($settingsPath, $settingsJson, $utf8WithoutBom)
|
||||
|
||||
$acl = Get-Acl -LiteralPath (Split-Path $settingsPath -Parent)
|
||||
$acl.SetAccessRuleProtection($true, $false)
|
||||
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
|
||||
'SYSTEM', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
|
||||
$acl.AddAccessRule([Security.AccessControl.FileSystemAccessRule]::new(
|
||||
'BUILTIN\Administrators', 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
|
||||
Set-Acl -LiteralPath (Split-Path $settingsPath -Parent) -AclObject $acl
|
||||
|
||||
New-Item -Path $classRegistryPath -Force | Out-Null
|
||||
Set-Item -Path $classRegistryPath -Value (Join-Path $installPath 'SGU.CredentialProvider.comhost.dll')
|
||||
New-ItemProperty -Path $classRegistryPath -Name ThreadingModel -Value Both -PropertyType String -Force | Out-Null
|
||||
|
||||
New-Item -Path $providerRegistryPath -Force | Out-Null
|
||||
Set-Item -Path $providerRegistryPath -Value 'SGU Institutional Login'
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
ProviderClassId = $providerClassId
|
||||
InstallPath = $installPath
|
||||
SettingsPath = $settingsPath
|
||||
Registered = Test-Path -LiteralPath $providerRegistryPath
|
||||
SystemPasswordProviderPreserved = $true
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidateSet('BrokerServer', 'CredentialProviderClient')]
|
||||
[string]$Role,
|
||||
|
||||
[string]$BrokerDnsName = 'sgu-auth.lci.lasalle.mx',
|
||||
[string]$OutputDirectory = "$env:PUBLIC\Documents\SGU-Certificates"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = [Security.Principal.WindowsPrincipal]::new($identity)
|
||||
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||
throw 'Run this script from an elevated PowerShell session.'
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
|
||||
|
||||
if ($Role -eq 'BrokerServer') {
|
||||
$certificate = New-SelfSignedCertificate `
|
||||
-DnsName $BrokerDnsName `
|
||||
-CertStoreLocation Cert:\LocalMachine\My `
|
||||
-KeyAlgorithm RSA -KeyLength 3072 -HashAlgorithm SHA256 `
|
||||
-KeyExportPolicy NonExportable `
|
||||
-NotAfter (Get-Date).AddYears(2) `
|
||||
-TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.1')
|
||||
$output = Join-Path $OutputDirectory 'sgu-auth-broker.cer'
|
||||
}
|
||||
else {
|
||||
$certificate = New-SelfSignedCertificate `
|
||||
-Subject 'CN=SGU Credential Provider Client' `
|
||||
-CertStoreLocation Cert:\LocalMachine\My `
|
||||
-KeyAlgorithm RSA -KeyLength 3072 -HashAlgorithm SHA256 `
|
||||
-KeyExportPolicy NonExportable `
|
||||
-NotAfter (Get-Date).AddYears(2) `
|
||||
-TextExtension @('2.5.29.37={text}1.3.6.1.5.5.7.3.2')
|
||||
$output = Join-Path $OutputDirectory 'sgu-credential-provider-client.cer'
|
||||
}
|
||||
|
||||
Export-Certificate -Cert $certificate -FilePath $output -Force | Out-Null
|
||||
# These certificates are self-signed end-entity certificates. Trust the public
|
||||
# half locally as well as on the peer so valid-only certificate lookup and the
|
||||
# local TLS server both reject expired/untrusted lab certificates deterministically.
|
||||
Import-Certificate -FilePath $output -CertStoreLocation Cert:\LocalMachine\Root | Out-Null
|
||||
[pscustomobject]@{
|
||||
Role = $Role
|
||||
Thumbprint = $certificate.Thumbprint
|
||||
PublicCertificatePath = $output
|
||||
PrivateKeyExportable = $false
|
||||
TrustedLocally = $certificate.Verify()
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$Configuration = 'Release',
|
||||
[string]$OutputRoot = (Join-Path $PSScriptRoot '..\artifacts')
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$repositoryRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
|
||||
$dotnet = (Get-Command dotnet -ErrorAction Stop).Source
|
||||
|
||||
$brokerOutput = Join-Path $OutputRoot 'broker'
|
||||
$providerOutput = Join-Path $OutputRoot 'credential-provider'
|
||||
|
||||
& $dotnet publish (Join-Path $repositoryRoot 'src\SGU.AuthBroker\SGU.AuthBroker.csproj') `
|
||||
--configuration $Configuration `
|
||||
--runtime win-x64 `
|
||||
--self-contained true `
|
||||
--output $brokerOutput
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Auth Broker publish failed.' }
|
||||
|
||||
& $dotnet publish (Join-Path $repositoryRoot 'src\SGU.CredentialProvider\SGU.CredentialProvider.csproj') `
|
||||
--configuration $Configuration `
|
||||
--runtime win-x64 `
|
||||
--self-contained false `
|
||||
--output $providerOutput
|
||||
if ($LASTEXITCODE -ne 0) { throw 'Credential Provider publish failed.' }
|
||||
|
||||
$requiredProviderFiles = @(
|
||||
'SGU.CredentialProvider.dll',
|
||||
'SGU.CredentialProvider.comhost.dll',
|
||||
'SGU.CredentialProvider.runtimeconfig.json',
|
||||
'Lithnet.CredentialProvider.dll',
|
||||
'SGU.AuthBroker.Core.dll'
|
||||
)
|
||||
foreach ($file in $requiredProviderFiles) {
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $providerOutput $file))) {
|
||||
throw "Credential Provider output is missing $file."
|
||||
}
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
BrokerOutput = (Resolve-Path $brokerOutput).Path
|
||||
CredentialProviderOutput = (Resolve-Path $providerOutput).Path
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ZoneName = 'lci.lasalle.mx',
|
||||
[string]$RecordName = 'sgu-auth',
|
||||
[ipaddress]$IPv4Address = '192.168.50.10',
|
||||
|
||||
[ipaddress[]]$ExternalForwarders = @()
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$existing = Get-DnsServerResourceRecord -ZoneName $ZoneName -Name $RecordName -RRType A -ErrorAction SilentlyContinue
|
||||
if ($existing) {
|
||||
$current = @($existing.RecordData.IPv4Address.IPAddressToString)
|
||||
if ($current -notcontains $IPv4Address.IPAddressToString) {
|
||||
throw "$RecordName.$ZoneName already exists with a different address: $($current -join ', ')."
|
||||
}
|
||||
}
|
||||
else {
|
||||
Add-DnsServerResourceRecordA -ZoneName $ZoneName -Name $RecordName -IPv4Address $IPv4Address
|
||||
}
|
||||
|
||||
if ($ExternalForwarders.Count -gt 0) {
|
||||
Set-DnsServerForwarder -IPAddress $ExternalForwarders -UseRootHint $false
|
||||
Clear-DnsServerCache -Force
|
||||
Clear-DnsClientCache
|
||||
}
|
||||
|
||||
[pscustomobject]@{
|
||||
BrokerRecord = Resolve-DnsName "$RecordName.$ZoneName" | Select-Object Name, Type, IPAddress
|
||||
ExternalForwarders = @(Get-DnsServerForwarder | Select-Object -ExpandProperty IPAddress | ForEach-Object IPAddressToString)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[ValidatePattern('^https://')]
|
||||
[string]$BrokerEndpoint,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string]$ClientCertificateThumbprint
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$credential = Get-Credential -Message 'Enter an institutional DO, AL, or AD account. The password is sent only to the configured mTLS broker.'
|
||||
$clave = $credential.UserName -replace '^.*\\', '' -replace '@.*$', ''
|
||||
$password = $credential.GetNetworkCredential().Password
|
||||
|
||||
$certificate = Get-ChildItem Cert:\LocalMachine\My |
|
||||
Where-Object Thumbprint -eq ($ClientCertificateThumbprint -replace ' ', '') |
|
||||
Select-Object -First 1
|
||||
if (-not $certificate -or -not $certificate.HasPrivateKey) {
|
||||
throw 'The client certificate with private key was not found in LocalMachine\My.'
|
||||
}
|
||||
|
||||
try {
|
||||
$body = @{ clave = $clave; password = $password } | ConvertTo-Json -Compress
|
||||
Invoke-RestMethod -Method Post -Uri $BrokerEndpoint -Certificate $certificate `
|
||||
-ContentType 'application/json' -Body $body -TimeoutSec 20
|
||||
}
|
||||
finally {
|
||||
$password = $null
|
||||
$body = $null
|
||||
$credential = $null
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
|
||||
param(
|
||||
[switch]$RemoveFiles
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$providerClassId = '{D789CFD8-5AD4-489F-9B83-7EB5D9D09335}'
|
||||
$installPath = Join-Path $env:ProgramFiles 'SGU\CredentialProvider'
|
||||
$providerRegistryPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\Credential Providers\$providerClassId"
|
||||
$classRegistryPath = "HKLM:\SOFTWARE\Classes\CLSID\$providerClassId"
|
||||
|
||||
if ($PSCmdlet.ShouldProcess($providerClassId, 'Unregister the SGU Credential Provider')) {
|
||||
Remove-Item -LiteralPath $providerRegistryPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
Remove-Item -LiteralPath $classRegistryPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
if ($RemoveFiles -and $PSCmdlet.ShouldProcess($installPath, 'Remove Credential Provider files')) {
|
||||
Remove-Item -LiteralPath $installPath -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Write-Output 'The built-in Windows password Credential Provider was not changed.'
|
||||
Reference in New Issue
Block a user