Add six-month domain and broker monitoring
This commit is contained in:
@@ -35,6 +35,10 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
string password,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
using IDisposable? logScope = logger.BeginScope(
|
||||
"InstitutionalUser={InstitutionalUser}; InstitutionalRole={InstitutionalRole}",
|
||||
identity.UserName,
|
||||
identity.Role);
|
||||
Uri authenticationUri = new(
|
||||
new Uri(options.Endpoint, UriKind.Absolute),
|
||||
options.AuthenticationPath);
|
||||
@@ -155,6 +159,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
}
|
||||
|
||||
logger.LogInformation(
|
||||
BrokerEventIds.SguAuthenticationAccepted,
|
||||
"SGU accepted credentials after an explicit NTLM challenge in {ElapsedMilliseconds} ms.",
|
||||
elapsed.ElapsedMilliseconds);
|
||||
return (null, continuationUri);
|
||||
@@ -184,6 +189,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
catch (OperationCanceledException) when (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.SguAuthenticationTimeout,
|
||||
"SGU NTLM authentication timed out after {ElapsedMilliseconds} ms.",
|
||||
elapsed.ElapsedMilliseconds);
|
||||
return (NtlmValidationResult.Unavailable("NTLM_TIMEOUT"), null);
|
||||
@@ -191,6 +197,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
catch (HttpRequestException exception)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.SguAuthenticationNetworkFailure,
|
||||
exception,
|
||||
"SGU NTLM authentication failed after {ElapsedMilliseconds} ms.",
|
||||
elapsed.ElapsedMilliseconds);
|
||||
@@ -347,6 +354,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
if (profile is null)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileHtmlUnexpected,
|
||||
"SGU returned a profile page for role {Role}, but no supported profile fields were found after {ElapsedMilliseconds} ms.",
|
||||
identity.Role,
|
||||
elapsed.ElapsedMilliseconds);
|
||||
@@ -354,8 +362,10 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
else
|
||||
{
|
||||
logger.LogInformation(
|
||||
"SGU profile enrichment completed for role {Role} in {ElapsedMilliseconds} ms.",
|
||||
BrokerEventIds.ProfileEnrichmentCompleted,
|
||||
"SGU profile enrichment completed for role {Role} with {ProfileFieldCount} supported fields in {ElapsedMilliseconds} ms.",
|
||||
identity.Role,
|
||||
CountProfileFields(profile),
|
||||
elapsed.ElapsedMilliseconds);
|
||||
}
|
||||
|
||||
@@ -382,6 +392,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileEnrichmentTimeout,
|
||||
"SGU profile request for role {Role} timed out after {ElapsedMilliseconds} ms.",
|
||||
identity.Role,
|
||||
elapsed.ElapsedMilliseconds);
|
||||
@@ -389,6 +400,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileEnrichmentFailure,
|
||||
exception,
|
||||
"SGU profile enrichment failed for role {Role} after {ElapsedMilliseconds} ms.",
|
||||
identity.Role,
|
||||
@@ -423,11 +435,33 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
GetProfileUri(path),
|
||||
allowedHosts,
|
||||
timeoutToken).ConfigureAwait(false);
|
||||
profile = profile.Overlay(html is null ? null : parser(html));
|
||||
if (html is null)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfilePageUnavailable,
|
||||
"Optional SGU profile page {Path} did not return usable HTML for role {Role}; preserving fields already collected.",
|
||||
path,
|
||||
role);
|
||||
continue;
|
||||
}
|
||||
|
||||
InstitutionalProfile? pageProfile = parser(html);
|
||||
if (pageProfile is null)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileHtmlUnexpected,
|
||||
"Optional SGU profile page {Path} returned HTML without its supported field IDs for role {Role}; preserving fields already collected.",
|
||||
path,
|
||||
role);
|
||||
continue;
|
||||
}
|
||||
|
||||
profile = profile.Overlay(pageProfile);
|
||||
}
|
||||
catch (OperationCanceledException) when (!requestCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileEnrichmentTimeout,
|
||||
"SGU optional staff profile enrichment for role {Role} reached its total timeout after {ElapsedMilliseconds} ms; preserving fields already collected.",
|
||||
role,
|
||||
elapsed.ElapsedMilliseconds);
|
||||
@@ -436,6 +470,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileEnrichmentFailure,
|
||||
exception,
|
||||
"An optional SGU staff profile page for role {Role} failed after {ElapsedMilliseconds} ms; preserving fields already collected.",
|
||||
role,
|
||||
@@ -487,6 +522,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
}
|
||||
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfilePageUnavailable,
|
||||
"Optional SGU profile page {Path} returned HTTP {StatusCode}.",
|
||||
requestedUri.AbsolutePath,
|
||||
statusCode);
|
||||
@@ -494,6 +530,7 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
}
|
||||
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfilePageUnavailable,
|
||||
"Optional SGU profile page {Path} exceeded the redirect limit.",
|
||||
requestedUri.AbsolutePath);
|
||||
return null;
|
||||
@@ -550,6 +587,23 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
right.AbsolutePath.TrimEnd('/'),
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
private static int CountProfileFields(InstitutionalProfile profile) =>
|
||||
new[]
|
||||
{
|
||||
profile.EmployeeNumber,
|
||||
profile.DisplayName,
|
||||
profile.GivenName,
|
||||
profile.Surname,
|
||||
profile.Email,
|
||||
profile.EmployeeType,
|
||||
profile.JobTitle,
|
||||
profile.Department,
|
||||
profile.StreetAddress,
|
||||
profile.City,
|
||||
profile.State,
|
||||
profile.PostalCode
|
||||
}.Count(value => !string.IsNullOrWhiteSpace(value));
|
||||
|
||||
private static async Task DrainResponseAsync(
|
||||
HttpResponseMessage response,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -604,17 +658,31 @@ public sealed class NtlmCredentialValidator : INtlmCredentialValidator
|
||||
response.Content,
|
||||
options.MaxProfileBytes,
|
||||
timeoutToken).ConfigureAwait(false);
|
||||
return identity.Role switch
|
||||
InstitutionalProfile? profile;
|
||||
switch (identity.Role)
|
||||
{
|
||||
InstitutionalRole.Administrative =>
|
||||
SguProfileParser.ParseAdministrative(html, identity.NumericId) ??
|
||||
SguProfileParser.ParseMenu(html),
|
||||
InstitutionalRole.Student =>
|
||||
SguProfileParser.ParseStudent(html, identity.NumericId) ??
|
||||
SguProfileParser.ParseMenu(html),
|
||||
InstitutionalRole.Professor => SguProfileParser.ParseMenu(html),
|
||||
_ => null
|
||||
};
|
||||
case InstitutionalRole.Administrative:
|
||||
profile = SguProfileParser.ParseAdministrative(html, identity.NumericId);
|
||||
break;
|
||||
case InstitutionalRole.Student:
|
||||
profile = SguProfileParser.ParseStudent(html, identity.NumericId);
|
||||
break;
|
||||
case InstitutionalRole.Professor:
|
||||
return SguProfileParser.ParseMenu(html);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (profile is not null)
|
||||
{
|
||||
return profile;
|
||||
}
|
||||
|
||||
logger.LogWarning(
|
||||
BrokerEventIds.ProfileHtmlUnexpected,
|
||||
"The primary SGU profile HTML did not contain the supported field IDs for role {Role}; attempting the menu-name fallback.",
|
||||
identity.Role);
|
||||
return SguProfileParser.ParseMenu(html);
|
||||
}
|
||||
|
||||
private static async Task<string> ReadLimitedStringAsync(
|
||||
|
||||
Reference in New Issue
Block a user