public UserTenantModel(UserTenantInput input) { this.PartitionKey = input.UserId; this.RowKey = input.Tenant; this.Roles = input.Roles; this.Name = input.Name; this.Type = input.Type; }
public async Task <UserTenantListModel> GetAllTenantsForUserAsync(string userId) { UserTenantInput input = new UserTenantInput { UserId = userId, }; return(await this.container.GetAllAsync(input)); }
public async Task <UserTenantListModel> DeleteAllAsync() { UserTenantInput input = new UserTenantInput { Tenant = this.GetTenantId(), }; return(await this.container.DeleteAllAsync(input)); }
public async Task <UserTenantModel> GetAsync(string userId) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = this.GetTenantId(), }; return(await this.container.GetAsync(input)); }
public async Task <UserTenantListModel> GetAllUsersForTenantAsync() { UserTenantInput input = new UserTenantInput { UserId = null, Tenant = this.GetTenantId(), }; return(await this.container.GetAllUsersAsync(input)); }
public async void UpdateReturnsExpectedUserTenant() { // Arrange this.someUserTenantInput = Builder <UserTenantInput> .CreateNew().Set(uti => uti.Roles, JsonConvert.SerializeObject(new[] { "someRole", "someOtherRole" })).Build(); // Act var result = await this.userTenantContainer.UpdateAsync(this.someUserTenantInput); // Assert this.AssertUserTenantMatchesInput(result); }
public async Task <UserTenantModel> PutAsync(string userId, [FromBody] UserTenantModel update) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = this.GetTenantId(), Roles = update.Roles, }; return(await this.container.UpdateAsync(input)); }
public async Task <UserTenantModel> InviteAsync([FromBody] Invitation invitation) { // Object to insert in table as placeholder UserTenantInput input = new UserTenantInput { UserId = Guid.NewGuid().ToString(), Tenant = this.GetTenantId(), Roles = JsonConvert.SerializeObject(new List <string>() { invitation.Role }), Name = invitation.EmailAddress, Type = "Invited", }; List <Claim> claims = new List <Claim>() { new Claim("role", invitation.Role), new Claim("tenant", this.GetTenantId()), new Claim("userId", input.UserId), new Claim("invitedby", this.GetClaimsUserDetails()), }; string forwardedFor = null; // add issuer with forwarded for address if exists (added by reverse proxy) if (this.HttpContext.Request.Headers.Where(t => t.Key == "X-Forwarded-For").Count() > 0) { forwardedFor = this.HttpContext.Request.Headers.Where(t => t.Key == "X-Forwarded-For").FirstOrDefault().Value .First(); } var jwtHandler = new JwtSecurityTokenHandler(); string inviteToken = jwtHandler.WriteToken(this.jwtHelper.MintToken(claims, "IdentityGateway", DateTime.Now.AddDays(3))); var msg = this.inviteHelper.CreateMessage(invitation, forwardedFor, inviteToken, this.HttpContext.Request.Host.ToString()); if (invitation.InviteUserMetaData != null) { await this.inviteHelper.CreateUserSettings(invitation, input.UserId); } // Send email var client = this.sendGridClientFactory.CreateSendGridClient(); var response = await client.SendEmailAsync(msg); // Add Audit Data,i.e, who invited the user input.CreatedBy = this.GetClaimsUserDetails(); input.CreatedTime = DateTime.UtcNow; return(await this.container.CreateAsync(input)); }
public async Task <UserTenantModel> PostAsync(string userId, [FromBody] UserTenantModel model) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = this.GetTenantId(), Roles = model.Roles, Name = model.Name, Type = model.Type, }; return(await this.container.CreateAsync(input)); }
private async Task <bool> DoesUserHaveAnyOtherTenants(string userId) { UserTenantInput input = new UserTenantInput { UserId = userId, }; var otherTenantForUser = await this.container.GetAllAsync(input); if (otherTenantForUser != null && otherTenantForUser.Models != null && otherTenantForUser.Models.Any()) { return(true); } return(false); }
public async Task <UserTenantModel> PostAsync(string userId, [FromBody] UserTenantModel model) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = this.GetTenantId(), Roles = model.Roles, Name = model.Name, Type = string.IsNullOrWhiteSpace(model.Type) ? "Member" : model.Type, CreatedTime = DateTime.UtcNow, CreatedBy = !string.IsNullOrWhiteSpace(model.CreatedBy) ? model.CreatedBy : this.GetCreatedBy(), }; return(await this.container.CreateAsync(input)); }
public async Task <UserTenantModel> DeleteAsync(string userId) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = this.GetTenantId(), }; var userTenantModel = await this.container.DeleteAsync(input); if (userTenantModel != null) { await this.CleanupUserSettingsIfUserHasNoOtherTenants(userTenantModel); } return(userTenantModel); }
public async Task <IActionResult> PostTokenAsync( [FromBody] ClientCredentialInput input) { string resourceUri = "https://graph.microsoft.com/"; ClientCredential clientCredential = new ClientCredential(input.ClientId, input.ClientSecret); try { AuthenticationResult token = await this.authenticationContext.AcquireTokenAsync(resourceUri, clientCredential); } catch (Exception e) { return(this.StatusCode(401, e.Message)); } UserTenantInput tenantInput = new UserTenantInput { UserId = input.ClientId, Tenant = input.Scope, }; UserTenantListModel tenantsModel = await this.userTenantContainer.GetAllAsync(tenantInput); if (tenantsModel.Models.Count == 0) { throw new Exception("Not granted access to that tenant"); } DateTime?expirationTime = null; if (this.config.Global.ClientAuth.TokenExpirationDuration != null) { expirationTime = DateTime.Now.Add(TimeSpan.ParseExact(this.config.Global.ClientAuth.TokenExpirationDuration, "c", null)); } // if successful, then mint token var jwtHandler = new JwtSecurityTokenHandler(); var claims = new List <Claim>(); claims.Add(new Claim("client_id", input.ClientId)); claims.Add(new Claim("sub", input.ClientId)); claims.Add(new Claim("name", input.ClientId)); claims.Add(new Claim("type", "Client Credentials")); string tokenString = jwtHandler.WriteToken(await this.jwtHelper.GetIdentityToken(claims, input.Scope, "IoTPlatform", expirationTime)); return(this.StatusCode(200, tokenString)); }
public async Task <UserTenantListModel> DeleteAllAsync() { UserTenantInput input = new UserTenantInput { Tenant = this.GetTenantId(), }; var userTenants = await this.container.DeleteAllAsync(input); if (userTenants != null && userTenants.Models != null && userTenants.Models.Any()) { foreach (UserTenantModel userTenantModel in userTenants.Models) { await this.CleanupUserSettingsIfUserHasNoOtherTenants(userTenantModel); } } return(userTenants); }
public async Task <ActionResult> PostAsync([FromHeader(Name = "Authorization")] string authHeader, [FromRoute] string tenant) { if (authHeader == null || !authHeader.StartsWith("Bearer")) { throw new NoAuthorizationException("No Bearer Token Authorization Header was passed."); } // Extract Bearer token string encodedToken = authHeader.Substring("Bearer ".Length).Trim(); var jwtHandler = new JwtSecurityTokenHandler(); if (!this.jwtHelper.TryValidateToken("IoTPlatform", encodedToken, this.HttpContext, out JwtSecurityToken jwt)) { throw new NoAuthorizationException("The given token could not be read or validated."); } if (jwt?.Claims?.Count(c => c.Type == "sub") == 0) { throw new NoAuthorizationException("Not allowed access. No User Claims"); } // Create a userTenantInput for the purpose of finding if the user has access to the space UserTenantInput tenantInput = new UserTenantInput { UserId = jwt?.Claims?.Where(c => c.Type == "sub").First()?.Value, Tenant = tenant, }; UserTenantModel tenantResult = await this.userTenantContainer.GetAsync(tenantInput); if (tenantResult != null) { // Everything checks out so you can mint a new token var tokenString = jwtHandler.WriteToken(await this.jwtHelper.GetIdentityToken(jwt.Claims.Where(c => new List <string>() { "sub", "name", "email" }.Contains(c.Type)).ToList(), tenant, jwt.Audiences.First(), jwt.ValidTo)); return(this.StatusCode(200, tokenString)); } else { throw new NoAuthorizationException("Not allowed access to this tenant."); } }
private async Task AddUserForPendingTenants(SystemAdminModel result, string createdBy) { if (result != null) { TenantListModel activeTenants = await this.userTenantcontainer.GetAllActiveTenantAsync(); if (activeTenants != null && activeTenants.Models != null && activeTenants.Models.Count > 0) { List <string> activeTenantIds = activeTenants.Models.Select(x => x.TenantId).ToList(); if (activeTenantIds != null && activeTenantIds.Count > 0) { UserTenantInput userInput = new UserTenantInput() { UserId = result.PartitionKey, Name = result.Name, Roles = JsonConvert.SerializeObject(new List <string>() { AdminRole }), Type = MemberType, }; UserTenantListModel existingTenants = await this.userTenantcontainer.GetAllAsync(userInput); for (int i = 0; i < activeTenantIds.Count; i++) { userInput.Tenant = activeTenantIds[i]; if (existingTenants != null && existingTenants.Models != null && existingTenants.Models.FirstOrDefault(x => x.UserId == userInput.UserId && x.TenantId == userInput.Tenant) == null) { // Adding Audit Data userInput.CreatedBy = createdBy; userInput.CreatedTime = DateTime.UtcNow; var createdUser = await this.userTenantcontainer.CreateAsync(userInput); } } } } } }
public async Task <JwtSecurityToken> GetIdentityToken(List <Claim> claims, string tenant, string audience, DateTime?expiration) { // add iat claim var timeSinceEpoch = DateTime.UtcNow.ToEpochTime(); claims.Add(new Claim("iat", timeSinceEpoch.ToString(), ClaimValueTypes.Integer)); var userId = claims.First(t => t.Type == "sub").Value; // Create a userTenantInput for the purpose of finding the full tenant list associated with this user UserTenantInput tenantInput = new UserTenantInput { UserId = userId, }; UserTenantListModel tenantsModel = await this.userTenantContainer.GetAllAsync(tenantInput); List <UserTenantModel> tenantList = tenantsModel.Models; // User did not specify the tenant to log into so get the default or last used if (string.IsNullOrEmpty(tenant)) { // authState has no tenant, so we should use either the User's last used tenant, or the first tenant available to them // Create a UserSettingsInput for the purpose of finding the LastUsedTenant setting for this user this.logger.LogInformation("User did not specify Tenant so default/last used tenant is set."); UserSettingsInput settingsInput = new UserSettingsInput { UserId = userId, SettingKey = "LastUsedTenant", }; UserSettingsModel lastUsedSetting = await this.userSettingsContainer.GetAsync(settingsInput); // Has last used tenant and it is in the list if (lastUsedSetting != null && tenantList.Count(t => t.TenantId == lastUsedSetting.Value) > 0) { tenant = lastUsedSetting.Value; } if (string.IsNullOrEmpty(tenant) && tenantList.Count > 0) { tenant = tenantList.First() .TenantId; // Set the tenant to the first tenant in the list of tenants for this user } } // If User not associated with Tenant then dont add claims return token without if (tenant != null) { UserTenantInput input = new UserTenantInput { UserId = userId, Tenant = tenant, }; UserTenantModel tenantModel = await this.userTenantContainer.GetAsync(input); // Add Tenant claims.Add(new Claim("tenant", tenantModel.TenantId)); // Add Roles tenantModel.RoleList.ForEach(role => claims.Add(new Claim("role", role))); // Settings Update LastUsedTenant UserSettingsInput settingsInput = new UserSettingsInput { UserId = claims.Where(c => c.Type == "sub").First().Value, SettingKey = "LastUsedTenant", Value = tenant, }; // Update if name is not the same await this.userSettingsContainer.UpdateAsync(settingsInput); if (tenantModel.Name != claims.Where(c => c.Type == "name").First().Value) { input.Name = claims.Where(c => c.Type == "name").First().Value; await this.userTenantContainer.UpdateAsync(input); } } DateTime expirationDateTime = expiration ?? DateTime.Now.AddDays(30); // add all tenants they have access to claims.AddRange(tenantList.Select(t => new Claim("available_tenants", t.TenantId))); // Token to String so you can use it in your client var token = this.MintToken(claims, audience, expirationDateTime); return(token); }
public async Task <UserTenantModel> InviteAsync([FromBody] Invitation invitation) { // Object to insert in table as placeholder UserTenantInput input = new UserTenantInput { UserId = Guid.NewGuid().ToString(), Tenant = this.GetTenantId(), Roles = JsonConvert.SerializeObject(new List <string>() { invitation.Role }), Name = invitation.EmailAddress, Type = "Invited", }; List <Claim> claims = new List <Claim>() { new Claim("role", invitation.Role), new Claim("tenant", this.GetTenantId()), new Claim("userId", input.UserId), }; string forwardedFor = null; // add issuer with forwarded for address if exists (added by reverse proxy) if (this.HttpContext.Request.Headers.Where(t => t.Key == "X-Forwarded-For").Count() > 0) { forwardedFor = this.HttpContext.Request.Headers.Where(t => t.Key == "X-Forwarded-For").FirstOrDefault().Value .First(); } var jwtHandler = new JwtSecurityTokenHandler(); string inviteToken = jwtHandler.WriteToken(this.jwtHelper.MintToken(claims, "IdentityGateway", DateTime.Now.AddDays(3))); var msg = new SendGridMessage(); msg.SetFrom(new EmailAddress("*****@*****.**", "3M IoT Platform Team")); var recipients = new List <EmailAddress> { new EmailAddress(invitation.EmailAddress), }; msg.AddTos(recipients); var assembly = Assembly.GetExecutingAssembly(); var resourceName = "Mmm.Iot.IdentityGateway.WebService.files.InviteEmail.html"; Func <IDictionary <string, object>, string> template; // Load the email template from file using (Stream stream = assembly.GetManifestResourceStream(resourceName)) using (StreamReader reader = new StreamReader(stream)) { template = Mustachio.Parser.Parse(reader.ReadToEnd()); } msg.SetSubject("Invitation to IoT Platform"); Uri uri = new Uri(forwardedFor ?? "https://" + this.HttpContext.Request.Host.ToString()); // Set the model for the template dynamic model = new ExpandoObject(); model.link = uri.AbsoluteUri + "#invite=" + inviteToken; // Set the content by doing a render on the template with the model msg.AddContent(MimeType.Html, template(model)); // Send email var client = this.sendGridClientFactory.CreateSendGridClient(); var response = await client.SendEmailAsync(msg); return(await this.container.CreateAsync(input)); }
public async Task <object> PostAsync( [FromForm] string state, [FromForm] string id_token, [FromForm] string error, [FromForm] string error_description) { if (!string.IsNullOrEmpty(error)) { // If there was an error returned from B2C, throw it as an expcetion throw new Exception($"Azure B2C returned an error: {{{error}: {error_description}}}"); } AuthState authState = null; try { authState = JsonConvert.DeserializeObject <AuthState>(state); } catch (Exception e) { throw new Exception("Invlid state from authentication redirect", e); } var originalAudience = authState.ClientId; // Bring over Subject and Name var jwtHandler = new JwtSecurityTokenHandler(); var jwt = jwtHandler.ReadJwtToken(id_token); var claims = jwt.Claims.Where(t => new List <string> { "sub", "name" }.Contains(t.Type)).ToList(); string invitedTenant = authState.Tenant; string userNameOrEmail = string.Empty; // If theres an invitation token then add user to tenant if (!string.IsNullOrEmpty(authState.Invitation)) { var inviteJWT = jwtHandler.ReadJwtToken(authState.Invitation); string inviteUserId = inviteJWT.Claims.Where(c => c.Type == "userId").First().Value; string newUserId = claims.Where(c => c.Type == "sub").First().Value; invitedTenant = inviteJWT.Claims.Where(c => c.Type == "tenant").First().Value; // Extract first email var emailClaim = jwt.Claims.Where(t => t.Type == "emails").FirstOrDefault(); if (emailClaim != null) { claims.Add(new Claim("email", emailClaim.Value)); var userName = claims.Where(c => c.Type == "name").FirstOrDefault(); if (userName == null) { // Adding the name to claims as the update name code block fails inside method GetIdentityToken in JwtHelpers claims.Add(new Claim("name", emailClaim.Value)); userNameOrEmail = emailClaim.Value; } else { userNameOrEmail = userName.Value; } } UserTenantInput userTenant = new UserTenantInput() { UserId = newUserId, Tenant = invitedTenant, Roles = JsonConvert.SerializeObject(inviteJWT.Claims.Where(c => c.Type == "role").Select(c => c.Value).ToList()), Type = "Member", Name = userNameOrEmail, }; await this.userTenantContainer.UpdateAsync(userTenant); UserSettingsInput userSettings = new UserSettingsInput() { UserId = newUserId, SettingKey = "inviteId", Value = inviteUserId, }; // Store invite Id await this.userSettingsContainer.UpdateAsync(userSettings); // Transfer settings to new userID await this.userSettingsContainer.UpdateUserIdAsync(inviteUserId, newUserId); try { // Delete placeholder for invite userTenant.UserId = inviteUserId; await this.userTenantContainer.DeleteAsync(userTenant); } catch (Exception) { // Do nothing, delete will fail only for scenarios where the update is incorrectly working or if the invited user gets deleted for some reason. // Not throwing an exception because a delete failure will not break the code however there will be some unused invites } } if (!string.IsNullOrEmpty(authState.Nonce)) { claims.Add(new Claim("nonce", authState.Nonce)); } string tokenString = jwtHandler.WriteToken(await this.jwtHelper.GetIdentityToken(claims, invitedTenant, originalAudience, null)); // Build Return Uri var returnUri = new UriBuilder(authState.ReturnUrl); // Need to build Query carefully to not clobber other query items -- just injecting state // var query = HttpUtility.ParseQueryString(returnUri.Query); // query["state"] = HttpUtility.UrlEncode(authState.state); // returnUri.Query = query.ToString(); returnUri.Fragment = "id_token=" + tokenString + "&state=" + HttpUtility.UrlEncode(authState .State); // pass token in Fragment for more security (Browser wont forward...) return(this.Redirect(returnUri.Uri.ToString())); }
public async Task <IActionResult> PostAsync( [FromForm] string state, [FromForm] string id_token, [FromForm] string error, [FromForm] string error_description) { if (!string.IsNullOrEmpty(error)) { // If there was an error returned from B2C, throw it as an expcetion throw new Exception($"Azure B2C returned an error: {{{error}: {error_description}}}"); } AuthState authState = null; try { authState = JsonConvert.DeserializeObject <AuthState>(state); } catch (Exception e) { throw new Exception("Invlid state from authentication redirect", e); } var originalAudience = authState.ClientId; // Bring over Subject and Name var jwtHandler = new JwtSecurityTokenHandler(); var jwt = jwtHandler.ReadJwtToken(id_token); var claims = jwt.Claims.Where(t => new List <string> { "sub", "name" }.Contains(t.Type)).ToList(); // If theres an invitation token then add user to tenant if (!string.IsNullOrEmpty(authState.Invitation)) { var inviteJWT = jwtHandler.ReadJwtToken(authState.Invitation); UserTenantInput userTenant = new UserTenantInput() { UserId = claims.Where(c => c.Type == "sub").First().Value, Tenant = inviteJWT.Claims.Where(c => c.Type == "tenant").First().Value, Roles = JsonConvert.SerializeObject(inviteJWT.Claims.Where(c => c.Type == "role").Select(c => c.Value).ToList()), Type = "Member", }; await this.userTenantContainer.UpdateAsync(userTenant); // Delete placeholder for invite userTenant.UserId = inviteJWT.Claims.Where(c => c.Type == "userId").First().Value; await this.userTenantContainer.DeleteAsync(userTenant); } // Extract first email var emailClaim = jwt.Claims.Where(t => t.Type == "emails").FirstOrDefault(); if (emailClaim != null) { claims.Add(new Claim("email", emailClaim.Value)); } if (!string.IsNullOrEmpty(authState.Nonce)) { claims.Add(new Claim("nonce", authState.Nonce)); } string tokenString = jwtHandler.WriteToken(await this.jwtHelper.GetIdentityToken(claims, authState.Tenant, originalAudience, null)); // Build Return Uri var returnUri = new UriBuilder(authState.ReturnUrl); // Need to build Query carefully to not clobber other query items -- just injecting state // var query = HttpUtility.ParseQueryString(returnUri.Query); // query["state"] = HttpUtility.UrlEncode(authState.state); // returnUri.Query = query.ToString(); returnUri.Fragment = "id_token=" + tokenString + "&state=" + HttpUtility.UrlEncode(authState .State); // pass token in Fragment for more security (Browser wont forward...) return(this.Redirect(returnUri.Uri.ToString())); }