-
Notifications
You must be signed in to change notification settings - Fork 367
Adding FMI source to MI app #5299
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7e29a8b
44e34c5
d1c9a99
615e5d4
951943f
784d438
d60e236
2b3c614
d14ee0c
2caf549
d3318fc
cf3f3bc
8d21f66
62095ff
5c1c4dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,34 +16,79 @@ internal class ServiceFabricManagedIdentitySource : AbstractManagedIdentity | |
private const string ServiceFabricMsiApiVersion = "2019-07-01-preview"; | ||
private readonly Uri _endpoint; | ||
private readonly string _identityHeaderValue; | ||
private readonly bool _isFmiCredentialRequest; | ||
private static string _mitsEndpointFmiPath => "/metadata/identity/oauth2/fmi/credential"; | ||
|
||
internal static Lazy<HttpClient> _httpClientLazy; | ||
|
||
public static AbstractManagedIdentity Create(RequestContext requestContext) | ||
public static AbstractManagedIdentity Create(RequestContext requestContext, bool isFmiCredentialRequest = false) | ||
{ | ||
Uri endpointUri; | ||
string identityEndpoint = EnvironmentVariables.IdentityEndpoint; | ||
|
||
requestContext.Logger.Info(() => "[Managed Identity] Service fabric managed identity is available."); | ||
|
||
if (!Uri.TryCreate(identityEndpoint, UriKind.Absolute, out Uri endpointUri)) | ||
if (isFmiCredentialRequest) | ||
{ | ||
VerifyFederatedEnvVariablesAreAvailable(); | ||
requestContext.Logger.Info(() => "[Managed Identity] Service fabric federated managed identity is available."); | ||
identityEndpoint = EnvironmentVariables.FmiServiceFabricEndpoint; | ||
requestContext.Logger.Info(() => "[Managed Identity] Using FMI Service fabric endpoint."); | ||
|
||
if (!Uri.TryCreate(identityEndpoint + _mitsEndpointFmiPath, UriKind.Absolute, out endpointUri)) | ||
{ | ||
string errorMessage = string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityEndpointInvalidUriError, | ||
"APP_IDENTITY_ENDPOINT", identityEndpoint, "FMI Service Fabric"); | ||
|
||
throw MsalServiceExceptionFactory.CreateManagedIdentityException( | ||
MsalError.InvalidManagedIdentityEndpoint, | ||
errorMessage, | ||
null, | ||
ManagedIdentitySource.ServiceFabric, | ||
null); | ||
} | ||
} | ||
else | ||
{ | ||
string errorMessage = string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityEndpointInvalidUriError, | ||
"IDENTITY_ENDPOINT", identityEndpoint, "Service Fabric"); | ||
|
||
// Use the factory to create and throw the exception | ||
var exception = MsalServiceExceptionFactory.CreateManagedIdentityException( | ||
MsalError.InvalidManagedIdentityEndpoint, | ||
errorMessage, | ||
null, | ||
ManagedIdentitySource.ServiceFabric, | ||
null); | ||
|
||
throw exception; | ||
requestContext.Logger.Info(() => "[Managed Identity] Service fabric managed identity is available."); | ||
|
||
if (!Uri.TryCreate(identityEndpoint, UriKind.Absolute, out endpointUri)) | ||
{ | ||
string errorMessage = string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityEndpointInvalidUriError, | ||
"IDENTITY_ENDPOINT", identityEndpoint, "Service Fabric"); | ||
|
||
throw MsalServiceExceptionFactory.CreateManagedIdentityException( | ||
MsalError.InvalidManagedIdentityEndpoint, | ||
errorMessage, | ||
null, | ||
ManagedIdentitySource.ServiceFabric, | ||
null); | ||
} | ||
} | ||
|
||
requestContext.Logger.Verbose(() => "[Managed Identity] Creating Service Fabric managed identity. Endpoint URI: " + identityEndpoint); | ||
|
||
return new ServiceFabricManagedIdentitySource(requestContext, endpointUri, EnvironmentVariables.IdentityHeader); | ||
requestContext.Logger.Verbose(() => $"[Managed Identity] Creating Service Fabric {(isFmiCredentialRequest ? "federated" : "")} managed identity. Endpoint URI: {identityEndpoint}"); | ||
|
||
return new ServiceFabricManagedIdentitySource(requestContext, endpointUri, EnvironmentVariables.IdentityHeader, isFmiCredentialRequest); | ||
} | ||
|
||
private static void VerifyFederatedEnvVariablesAreAvailable() | ||
{ | ||
if (string.IsNullOrEmpty(EnvironmentVariables.IdentityServerThumbprint)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityFmiInvalidEnvVariableError, | ||
"IDENTITY_SERVER_THUMBPRINT")); | ||
} | ||
if (string.IsNullOrEmpty(EnvironmentVariables.FmiServiceFabricEndpoint)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityFmiInvalidEnvVariableError, | ||
"APP_IDENTITY_ENDPOINT")); | ||
} | ||
if (string.IsNullOrEmpty(EnvironmentVariables.IdentityHeader)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityFmiInvalidEnvVariableError, | ||
"IDENTITY_HEADER")); | ||
} | ||
if (string.IsNullOrEmpty(EnvironmentVariables.FmiServiceFabricApiVersion)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, MsalErrorMessage.ManagedIdentityFmiInvalidEnvVariableError, | ||
"IDENTITY_API_VERSION", "FMI Service Fabric")); | ||
} | ||
} | ||
|
||
internal override Func<HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool> GetValidationCallback() | ||
|
@@ -62,11 +107,12 @@ private bool ValidateServerCertificateCallback(HttpRequestMessage message, X509C | |
return string.Equals(certificate.GetCertHashString(), EnvironmentVariables.IdentityServerThumbprint, StringComparison.OrdinalIgnoreCase); | ||
} | ||
|
||
private ServiceFabricManagedIdentitySource(RequestContext requestContext, Uri endpoint, string identityHeaderValue) : | ||
base(requestContext, ManagedIdentitySource.ServiceFabric) | ||
private ServiceFabricManagedIdentitySource(RequestContext requestContext, Uri endpoint, string identityHeaderValue, bool isFmi) : | ||
base(requestContext, ManagedIdentitySource.ServiceFabric) | ||
{ | ||
_endpoint = endpoint; | ||
_identityHeaderValue = identityHeaderValue; | ||
_isFmiCredentialRequest = isFmi; | ||
|
||
if (requestContext.ServiceBundle.Config.ManagedIdentityId.IsUserAssigned) | ||
{ | ||
|
@@ -77,31 +123,43 @@ private ServiceFabricManagedIdentitySource(RequestContext requestContext, Uri en | |
protected override ManagedIdentityRequest CreateRequest(string resource) | ||
{ | ||
ManagedIdentityRequest request = new ManagedIdentityRequest(HttpMethod.Get, _endpoint); | ||
|
||
request.Headers["secret"] = _identityHeaderValue; | ||
|
||
request.QueryParameters["api-version"] = ServiceFabricMsiApiVersion; | ||
request.QueryParameters["resource"] = resource; | ||
|
||
switch (_requestContext.ServiceBundle.Config.ManagedIdentityId.IdType) | ||
if (_isFmiCredentialRequest) | ||
{ | ||
case AppConfig.ManagedIdentityIdType.ClientId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned client id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityClientId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
|
||
case AppConfig.ManagedIdentityIdType.ResourceId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned resource id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityResourceId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
|
||
case AppConfig.ManagedIdentityIdType.ObjectId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned object id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityObjectId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
_requestContext.Logger.Info("[Managed Identity] Request is for FMI, no ids or resource will be added to the request."); | ||
request.QueryParameters["api-version"] = EnvironmentVariables.FmiServiceFabricApiVersion; | ||
} | ||
else | ||
{ | ||
request.QueryParameters["api-version"] = ServiceFabricMsiApiVersion; | ||
request.QueryParameters["resource"] = resource; | ||
|
||
switch (_requestContext.ServiceBundle.Config.ManagedIdentityId.IdType) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldnt there be a default case? We could log the unexpected, that could give us additional information when troubleshooting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically yes, but this ID is a required parameter for the managed identity application. So it will always be set to something unless there is a catastrophic failure. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's still a good practice to throw an exception @trwalke on default. It will protect against a case where a new enum parameter is added. |
||
{ | ||
case AppConfig.ManagedIdentityIdType.ClientId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned client id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityClientId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
|
||
case AppConfig.ManagedIdentityIdType.ResourceId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned resource id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityResourceId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
|
||
case AppConfig.ManagedIdentityIdType.ObjectId: | ||
_requestContext.Logger.Info("[Managed Identity] Adding user assigned object id to the request."); | ||
request.QueryParameters[Constants.ManagedIdentityObjectId] = _requestContext.ServiceBundle.Config.ManagedIdentityId.UserAssignedId; | ||
break; | ||
} | ||
} | ||
|
||
return request; | ||
} | ||
|
||
internal string GetEndpointForTesting() | ||
{ | ||
return _endpoint.ToString(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need an extra param here? Can't ManagedIdentity classes deal with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can do that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, it is better to keep this here because there the check is needed for the experimental features to be enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with dropping the experimental feature flag if it leads to better design.