public UserTenantModel(UserTenantInput input)
 {
     this.PartitionKey = input.UserId;
     this.RowKey       = input.Tenant;
     this.Roles        = input.Roles;
     this.Name         = input.Name;
     this.Type         = input.Type;
 }
Beispiel #2
0
        public async Task <UserTenantListModel> GetAllTenantsForUserAsync(string userId)
        {
            UserTenantInput input = new UserTenantInput
            {
                UserId = userId,
            };

            return(await this.container.GetAllAsync(input));
        }
Beispiel #3
0
        public async Task <UserTenantListModel> DeleteAllAsync()
        {
            UserTenantInput input = new UserTenantInput
            {
                Tenant = this.GetTenantId(),
            };

            return(await this.container.DeleteAllAsync(input));
        }
Beispiel #4
0
        public async Task <UserTenantModel> GetAsync(string userId)
        {
            UserTenantInput input = new UserTenantInput
            {
                UserId = userId,
                Tenant = this.GetTenantId(),
            };

            return(await this.container.GetAsync(input));
        }
Beispiel #5
0
        public async Task <UserTenantListModel> GetAllUsersForTenantAsync()
        {
            UserTenantInput input = new UserTenantInput
            {
                UserId = null,
                Tenant = this.GetTenantId(),
            };

            return(await this.container.GetAllUsersAsync(input));
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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));
        }
Beispiel #8
0
        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));
        }
Beispiel #9
0
        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));
        }
Beispiel #10
0
        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);
        }
Beispiel #11
0
        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));
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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));
        }
Beispiel #14
0
        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);
        }
Beispiel #15
0
        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.");
            }
        }
Beispiel #16
0
        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);
                            }
                        }
                    }
                }
            }
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
        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));
        }
Beispiel #19
0
        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()));
        }