/// <summary> /// Returns a PnPConnection based on connecting using an existing token /// </summary> /// <param name="token">Token to connect with</param> /// <param name="tokenAudience">Indicator of <see cref="TokenAudience"/> indicating for which API this token is meant to be used</param> /// <param name="host">PowerShell Host environment in which the commands are being run</param> /// <param name="initializationType">Indicator of type <see cref="InitializationType"/> which indicates the method used to set up the connection. Used for gathering usage analytics.</param> /// <param name="url">Url of the SharePoint environment to connect to, if applicable. Leave NULL not to connect to a SharePoint environment.</param> /// <param name="clientContext">A SharePoint ClientContext to make available within this connection. Leave NULL to not connect to a SharePoint environment.</param> /// <param name="minimalHealthScore">Minimum health score that the SharePoint server should report before allowing requests to be executed on it. Scale of 0 to 10 where 0 is healthiest and 10 is least healthy. Leave NULL not to perform health checks on SharePoint.</param> /// <param name="pnpVersionTag">Identifier set on the SharePoint ClientContext as the ClientTag to identify the source of the requests to SharePoint. Leave NULL not to set it.</param> /// <param name="disableTelemetry">Boolean indicating if telemetry on the commands being executed should be disabled. Telemetry is enabled by default.</param> /// <returns><see cref="PnPConnection"/ instance which can be used to communicate with one of the supported APIs</returns> public static PnPConnection GetConnectionWithToken(GenericToken token, TokenAudience tokenAudience, PSHost host, InitializationType initializationType, PSCredential credentials, string url = null, ClientContext clientContext = null, int?minimalHealthScore = null, string pnpVersionTag = null, bool disableTelemetry = false, AzureEnvironment azureEnvironment = AzureEnvironment.Production) { var connection = new PnPConnection(host, initializationType, url, clientContext, new Dictionary <TokenAudience, GenericToken>(1) { { tokenAudience, token } }, minimalHealthScore, pnpVersionTag, disableTelemetry) { ConnectionMethod = ConnectionMethod.AccessToken, Tenant = token.ParsedToken.Claims.FirstOrDefault(c => c.Type.Equals("tid", StringComparison.InvariantCultureIgnoreCase))?.Value, ClientId = token.ParsedToken.Claims.FirstOrDefault(c => c.Type.Equals("appid", StringComparison.InvariantCultureIgnoreCase))?.Value, AzureEnvironment = azureEnvironment }; connection.PSCredential = credentials; return(connection); }
/// <summary> /// Factory method for creating SWT tokens. /// </summary> /// <param name="issuer">The entity issuing the token.</param> /// <param name="audience">The entity receiving the token.</param> /// <param name="expiresOnUtc">The expiry time for the token in UTC.</param> /// <param name="signingKey">The token signing key.</param> /// <returns> /// An instance of SimpleWebToken class. /// </returns> public static SimpleWebToken CreateToken( TokenIssuer issuer, TokenAudience audience, DateTime expiresOnUtc, byte[] signingKey) { return(CreateToken(issuer, audience, expiresOnUtc, null, signingKey)); }
/// <summary> /// Gets the role claims for the current principal. /// </summary> /// <param name="issuer"></param> /// <param name="audience"></param> private IEnumerable <Claim> GetRoleClaims(TokenIssuer issuer, TokenAudience audience) { IDictionary <TokenIssuer, IList <string> > issuerToRolesMapping; if (this.audienceIssuerRolesMapping.TryGetValue(audience, out issuerToRolesMapping)) { IList <string> roles; if (issuerToRolesMapping.TryGetValue(issuer, out roles)) { IList <Claim> roleClaims = roles.Select(role => new Claim(HolMonClaimTypes.Role, role)).ToList(); return(roleClaims); } } return(new[] { new Claim(HolMonClaimTypes.Role, HolMonRoles.AnonymousAccessRole) }); }
/// <summary> /// Factory method for creating SWT tokens. Use this overload when there is a requirement /// to encode additional claims into the token. /// </summary> /// <param name="issuer">The entity issuing the token.</param> /// <param name="audience">The entity receiving the token.</param> /// <param name="expiresOnUtc">The expiry time for the token in UTC.</param> /// <param name="additionalClaims"> /// Any additional claims to be included as part of the token. /// </param> /// <param name="signingKey">The token signing key.</param> /// <returns> /// An instance of SimpleWebToken class. /// </returns> public static SimpleWebToken CreateToken( TokenIssuer issuer, TokenAudience audience, DateTime expiresOnUtc, IEnumerable <Claim> additionalClaims, byte[] signingKey) { if (issuer == TokenIssuer.Unknown) { throw new ArgumentException( "{0} is not not allowed as token issuer.".FormatInvariant(issuer.ToString())); } if (audience == TokenAudience.Unknown) { throw new ArgumentException( "{0} is not allowed as token audience.".FormatInvariant(audience.ToString())); } if (signingKey == null || signingKey.Length == 0) { throw new ArgumentException("The signing key is not valid."); } var claims = new List <Claim>(); if (additionalClaims != null) { claims.AddRange(additionalClaims); } var token = SimpleWebToken.Create(issuer.ToString(), audience.ToString(), expiresOnUtc, claims); token.SignToken(signingKey); return(token); }
private (bool valid, string message) ValidateTokenForPermissions(GenericToken token, TokenAudience tokenAudience, string[] orRoles = null, string[] andRoles = null, TokenType tokenType = TokenType.All) { bool valid = false; var message = string.Empty; if (tokenType != TokenType.All && token.TokenType != tokenType) { throw new PSSecurityException($"Access to {tokenAudience} failed because the API requires {(tokenType == TokenType.Application ? "an" : "a")} {tokenType} token while you currently use {(token.TokenType == TokenType.Application ? "an" : "a")} {token.TokenType} token."); } var andRolesMatched = false; if (andRoles != null && andRoles.Length != 0) { // we have explicitely required roles andRolesMatched = andRoles.All(r => token.Roles.Contains(r)); } else { andRolesMatched = true; } var orRolesMatched = false; if (orRoles != null && orRoles.Length != 0) { orRolesMatched = orRoles.Any(r => token.Roles.Contains(r)); } else { orRolesMatched = true; } if (orRolesMatched && andRolesMatched) { valid = true; } if (orRoles != null || andRoles != null) { if (!valid) { // Requested role was not part of the access token, throw an exception explaining which application registration is missing which role if (!orRolesMatched) { message += "for one of the following roles: " + string.Join(", ", orRoles); } if (!andRolesMatched) { message += (message != string.Empty ? ", and " : ", ") + "for all of the following roles: " + string.Join(", ", andRoles); } throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {message}"); } } return(valid, message); }
/// <summary> /// Adds the provided token to the available tokens in the current connection /// </summary> /// <param name="tokenAudience">Audience the token is for</param> /// <param name="token">The token to add</param> internal void AddToken(TokenAudience tokenAudience, GenericToken token) { AccessTokens[tokenAudience] = token; }
/// <summary> /// Tries to get a token for the provided audience /// </summary> /// <param name="tokenAudience">Audience to try to get a token for</param> /// <param name="orRoles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param> /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns> internal GenericToken TryGetToken(TokenAudience tokenAudience, AzureEnvironment azureEnvironment, string[] orRoles = null, string[] andRoles = null, TokenType tokenType = TokenType.All) { GenericToken token = null; switch (tokenAudience) { case TokenAudience.MicrosoftGraph: if (ConnectionMethod == ConnectionMethod.DeviceLogin || ConnectionMethod == ConnectionMethod.GraphDeviceLogin) { var officeManagementApiScopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray(); // Take the remaining scopes and try requesting them from the Microsoft Graph API var scopes = Scopes.Except(officeManagementApiScopes).ToArray(); token = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.PnPManagementShellClientId, scopes, DeviceLoginCallback(null, false), AzureEnvironment); } else { if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = GraphToken.AcquireApplicationToken(Tenant, ClientId, Certificate, AzureEnvironment); } else if (ClientSecret != null) { token = GraphToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret, AzureEnvironment); } else if (Scopes != null) { var officeManagementApiScopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray(); // Take the remaining scopes and try requesting them from the Microsoft Graph API var scopes = Scopes.Except(officeManagementApiScopes).ToArray(); if (scopes.Length > 0) { token = PSCredential == null?GraphToken.AcquireApplicationTokenInteractive(PnPManagementShellClientId, scopes, azureEnvironment) : GraphToken.AcquireDelegatedTokenWithCredentials(PnPManagementShellClientId, scopes, PSCredential.UserName, PSCredential.Password, azureEnvironment); } else { throw new PSSecurityException($"Access to {tokenAudience} failed because you did not connect with any permission scopes related to this service (for instance 'Group.Read.All')."); } } } } break; case TokenAudience.OfficeManagementApi: if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, Certificate, AzureEnvironment); } else if (ClientSecret != null) { token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret, AzureEnvironment); } else if (Scopes != null) { var scopes = Enum.GetNames(typeof(OfficeManagementApiPermission)).Select(s => s.Replace("_", ".")).Intersect(Scopes).ToArray(); // Take the remaining scopes and try requesting them from the Microsoft Graph API if (scopes.Length > 0) { token = PSCredential == null?OfficeManagementApiToken.AcquireApplicationTokenInteractive(PnPManagementShellClientId, scopes, azureEnvironment) : OfficeManagementApiToken.AcquireDelegatedTokenWithCredentials(PnPManagementShellClientId, scopes, PSCredential.UserName, PSCredential.Password, azureEnvironment); } else { throw new PSSecurityException($"Access to {tokenAudience} failed because you did not connect with any permission scopes related to this service (for instance 'ServiceHealth.Read')."); } } } break; case TokenAudience.SharePointOnline: // This is not a token type we can request on demand return(null); } if (token != null) { var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType); if (!validationResults.valid) { throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}"); } return(token); } // Didn't have a token yet and unable to retrieve one return(null); }
/// <summary> /// Tries to get an access token for the provided audience /// </summary> /// <param name="tokenAudience">Audience to try to get an access token for</param> /// <param name="roles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default roles assigned to clientId if not specified.</param> /// <returns>AccessToken for the audience or NULL if unable to retrieve an access token for the audience on the current connection</returns> internal string TryGetAccessToken(TokenAudience tokenAudience, string[] roles = null) { return(TryGetToken(tokenAudience, AzureEnvironment, roles)?.AccessToken); }
/// <summary> /// Tries to get a token for the provided audience /// </summary> /// <param name="tokenAudience">Audience to try to get a token for</param> /// <param name="orRoles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param> /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns> internal GenericToken TryGetToken(TokenAudience tokenAudience, string[] orRoles = null, string[] andRoles = null, TokenType tokenType = TokenType.All) { GenericToken token = null; //Validate if we have a token already //if (AccessTokens.ContainsKey(tokenAudience)) //{ // // We have a token already, ensure it is still valid // token = AccessTokens[tokenAudience]; // if (token.ExpiresOn > DateTime.Now) // { // var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType); // if (validationResults.valid) // { // return token; // } // throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}"); // } // // Token was no longer valid, proceed with trying to create a new token //} // We do not have a token for the requested audience yet or it was no longer valid, try to create (a new) one switch (tokenAudience) { case TokenAudience.MicrosoftGraph: if (ConnectionMethod == ConnectionMethod.DeviceLogin || ConnectionMethod == ConnectionMethod.GraphDeviceLogin) { token = GraphToken.AcquireApplicationTokenDeviceLogin(PnPConnection.DeviceLoginClientId, Scopes, DeviceLoginCallback(null, false)); } else { if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = GraphToken.AcquireApplicationToken(Tenant, ClientId, Certificate); } else if (ClientSecret != null) { token = GraphToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret); } else if (Scopes != null) { token = PSCredential == null?GraphToken.AcquireApplicationTokenInteractive(DeviceLoginClientId, Scopes) : GraphToken.AcquireDelegatedTokenWithCredentials(DeviceLoginClientId, Scopes, PSCredential.UserName, PSCredential.Password); } } } break; case TokenAudience.OfficeManagementApi: if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, Certificate); } else if (ClientSecret != null) { token = OfficeManagementApiToken.AcquireApplicationToken(Tenant, ClientId, ClientSecret); } } break; case TokenAudience.SharePointOnline: // This is not a token type we can request on demand return(null); } if (token != null) { var validationResults = ValidateTokenForPermissions(token, tokenAudience, orRoles, andRoles, tokenType); if (!validationResults.valid) { throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {validationResults.message}"); } // Managed to create a token for the requested audience, add it to our collection with tokens //AccessTokens[tokenAudience] = token; return(token); } // Didn't have a token yet and unable to retrieve one return(null); }
/// <summary> /// Tries to get a token for the provided audience /// </summary> /// <param name="tokenAudience">Audience to try to get a token for</param> /// <param name="roles">The specific roles to request access to (i.e. Group.ReadWrite.All). Optional, will use default groups assigned to clientId if not specified.</param> /// <returns><see cref="GenericToken"/> for the audience or NULL if unable to retrieve a token for the audience on the current connection</returns> internal GenericToken TryGetToken(TokenAudience tokenAudience, string[] roles = null) { GenericToken token = null; // Validate if we have a token already if (AccessTokens.ContainsKey(tokenAudience)) { // We have a token already, ensure it is still valid token = AccessTokens[tokenAudience]; if (token.ExpiresOn > DateTime.Now) { // Token is still valid, ensure we dont have specific roles to check for or the requested roles to execute the command are present in the token if (roles == null || roles.Length == 0 || roles.Any(r => token.Roles.Contains(r))) { return(token); } if (roles != null) { // Requested role was not part of the access token, throw an exception explaining which application registration is missing which role throw new PSSecurityException($"Access to {tokenAudience} failed because the app registration {ClientId} in tenant {Tenant} is not granted {(roles.Length != 1 ? "any of " : string.Empty)}the permission{(roles.Length != 1 ? "s" : string.Empty)} {string.Join(", ", roles).TrimEnd(new[] { ',', ' ' })}"); } } // Token was no longer valid, proceed with trying to create a new token } // We do not have a token for the requested audience yet or it was no longer valid, try to create (a new) one switch (tokenAudience) { case TokenAudience.MicrosoftGraph: if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = GraphToken.AcquireToken(Tenant, ClientId, Certificate); } else if (ClientSecret != null) { token = GraphToken.AcquireToken(Tenant, ClientId, ClientSecret); } } break; case TokenAudience.OfficeManagementApi: if (!string.IsNullOrEmpty(Tenant)) { if (Certificate != null) { token = OfficeManagementApiToken.AcquireToken(Tenant, ClientId, Certificate); } else if (ClientSecret != null) { token = OfficeManagementApiToken.AcquireToken(Tenant, ClientId, ClientSecret); } } break; case TokenAudience.SharePointOnline: // This is not a token type we can request on demand return(null); } if (token != null) { // Managed to create a token for the requested audience, add it to our collection with tokens AccessTokens[tokenAudience] = token; return(token); } // Didn't have a token yet and unable to retrieve one return(null); }