Example #1
0
        public async void UpdateSettingsForUserAsync()
        {
            // Arrange
            IdentityGatewayApiSettingModel model = new IdentityGatewayApiSettingModel {
                Value = this.value, UserId = this.userId
            };

            this.mockExternalRequestHelper.Setup(m => m.ProcessRequestAsync <IdentityGatewayApiSettingModel>(It.IsAny <HttpMethod>(), It.IsAny <string>(), null, null)).ReturnsAsync(model);

            // Act
            var result = await this.identityGatewayClient.UpdateSettingsForUserAsync(this.userId, this.tenantId, this.value);

            // Assert
            Assert.Equal(model.UserId, result.UserId);
            Assert.Equal(model.Value, result.Value);
        }
Example #2
0
        public async Task <CreateTenantModel> CreateTenantAsync(string tenantId, string userId)
        {
            /* Creates a new tenant */
            string iotHubName = this.FormatResourceName(this.iotHubNameFormat, tenantId);
            string dpsName    = this.FormatResourceName(this.dpsNameFormat, tenantId);

            // Create a new tenant and save it to table storage
            var tenant = new TenantModel(tenantId);

            await this.tableStorageClient.InsertAsync <TenantModel>(TenantTableId, tenant);

            // kick off provisioning runbooks
            await this.runbookHelper.CreateIotHub(tenantId, iotHubName, dpsName);

            // Give the requesting user an admin role to the new tenant
            try
            {
                await this.identityGatewayClient.AddTenantForUserAsync(userId, tenantId, CreatedRole);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to add user to tenant.", e);
            }

            // Update the userSettings table with the lastUsedTenant if there isn't already a lastUsedTenant
            IdentityGatewayApiSettingModel userSettings = null;

            try
            {
                userSettings = await this.identityGatewayClient.GetSettingsForUserAsync(userId, LastUsedSettingKey);
            }
            catch (Exception e)
            {
                throw new Exception("Could not access user settings for LastUsedTenant.", e);
            }

            if (userSettings == null)
            {
                // Set the last used tenant to be this new tenant
                try
                {
                    await this.identityGatewayClient.AddSettingsForUserAsync(userId, LastUsedSettingKey, tenantId);
                }
                catch (Exception e)
                {
                    throw new Exception("Could not set user settings for LastUsedTenant.", e);
                }
            }

            // Write tenant info cosmos db collection name to app config
            try
            {
                foreach (string collection in this.tenantCollections.Keys)
                {
                    string collectionKey = string.Format(this.appConfigCollectionKeyFormat, tenantId, collection);
                    string collectionId  = $"{collection}-{tenantId}";
                    await this.appConfigClient.SetValueAsync(collectionKey, collectionId);
                }
            }
            catch (Exception e)
            {
                // In order for a complete tenant creation, all app config keys must be created. throw an error if not
                throw new Exception($"Unable to add required collection ids to App Config for tenant {tenantId}", e);
            }

            try
            {
                await this.deviceGroupClient.CreateDefaultDeviceGroupAsync(tenantId);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to create the default device group for the new tenant.", e);
            }

            return(new CreateTenantModel(tenantId));
        }
Example #3
0
        public async Task <DeleteTenantModel> DeleteTenantAsync(string tenantId, string userId, bool ensureFullyDeployed = true)
        {
            Dictionary <string, bool> deletionRecord = new Dictionary <string, bool> {
            };

            // Load the tenant from table storage
            string      partitionKey = tenantId.Substring(0, 1);
            TenantModel tenant       = await this.tableStorageClient.RetrieveAsync <TenantModel>(TenantTableId, partitionKey, tenantId);

            if (tenant != null && !tenant.IsIotHubDeployed && ensureFullyDeployed)
            {
                // If the tenant iothub is not deployed, we should not be able to start the delete process
                // this will mean the tenant is not fully deployed, so some resources could be deployed after
                // the delete process has begun
                throw new Exception("The tenant exists but it has not been fully deployed. Please wait for the tenant to fully deploy before trying to delete.");
            }
            else if (tenant == null)
            {
                this.logger.LogInformation("The tenant {tenantId} could not be deleted from Table Storage because it does not exist or was not fully created.", tenantId);
                deletionRecord["tenantTableStorage"] = true;
            }
            else
            {
                try
                {
                    await this.tableStorageClient.DeleteAsync <TenantModel>(TenantTableId, tenant);

                    deletionRecord["tenantTableStorage"] = true;
                }
                catch (Exception e)
                {
                    this.logger.LogInformation(e, "Unable to delete info from table storage for tenant {tenantId}", tenantId);
                    deletionRecord["tableStorage"] = false;
                }
            }

            // delete the tenant from the user
            try
            {
                await this.identityGatewayClient.DeleteTenantForAllUsersAsync(tenantId);

                deletionRecord["userTableStorage"] = true;
            }
            catch (Exception e)
            {
                this.logger.LogInformation(e, "Unable to delete user-tenant relationships for tenant {tenantId} in the user table.", tenantId);
                deletionRecord["userTableStorage"] = false;
            }

            // update userSettings table LastUsedTenant if necessary
            try
            {
                IdentityGatewayApiSettingModel lastUsedTenant = await this.identityGatewayClient.GetSettingsForUserAsync(userId, "LastUsedTenant");

                if (lastUsedTenant.Value == tenantId)
                {
                    // update the LastUsedTenant to some null
                    await this.identityGatewayClient.UpdateSettingsForUserAsync(userId, "LastUsedTenant", string.Empty);
                }
            }
            catch (Exception e)
            {
                this.logger.LogInformation(e, "Unable to get the user {userId} LastUsedTenant setting, the setting will not be updated.", userId);
            }

            // Gather tenant information
            string iotHubName = this.FormatResourceName(this.iotHubNameFormat, tenantId);
            string dpsName    = this.FormatResourceName(this.dpsNameFormat, tenantId);

            // trigger delete iothub runbook
            try
            {
                await this.runbookHelper.DeleteIotHub(tenantId, iotHubName, dpsName);

                deletionRecord["iotHub"] = true;
            }
            catch (Exception e)
            {
                this.logger.LogInformation(e, "Unable to successfully trigger Delete IoTHub Runbook for tenant {tenantId}", tenantId);
                deletionRecord["iotHub"] = false;
            }

            string saJobName = this.FormatResourceName(this.streamAnalyticsNameFormat, tenantId);

            // trigger delete SA runbook
            try
            {
                await this.runbookHelper.DeleteAlerting(tenantId, saJobName);

                deletionRecord["alerting"] = true;
            }
            catch (Exception e)
            {
                this.logger.LogInformation(e, "Unable to successfully trigger Delete Alerting runbook for tenant {tenantId}", tenantId);
                deletionRecord["alerting"] = false;
            }

            // Delete collections
            foreach (KeyValuePair <string, string> collectionInfo in this.tenantCollections)
            {
                string collection             = collectionInfo.Key;
                string databaseId             = collectionInfo.Value;
                string collectionAppConfigKey = string.Format(this.appConfigCollectionKeyFormat, tenantId, collection);
                string collectionId           = string.Empty;
                try
                {
                    collectionId = this.appConfigClient.GetValue(collectionAppConfigKey);
                }
                catch (Exception e)
                {
                    this.logger.LogInformation(e, "Unable to retrieve the key {collectionKey} for a collection id in App Config for tenant {tenantId}", collectionAppConfigKey, tenantId);
                }

                if (string.IsNullOrEmpty(collectionId))
                {
                    this.logger.LogInformation("The collectionId was not set properly for tenant {tenantId} while attempting to delete the {collection} collection", collectionAppConfigKey, tenantId);

                    // Currently, the assumption for an unknown collection id is that it has been deleted.
                    // We can come to this conclusion by assuming that the app config key containing the collection id was already deleted.
                    // TODO: Determine a more explicit outcome for this scenario - jrb
                    deletionRecord[$"{collection}Collection"] = true;

                    // If the collectionId could not be properly retrieved, go on to the next colleciton, do not attempt to delete.
                    continue;
                }

                try
                {
                    await this.cosmosClient.DeleteCollectionAsync(databaseId, collectionId);

                    deletionRecord[$"{collection}Collection"] = true;
                }
                catch (ResourceNotFoundException e)
                {
                    this.logger.LogInformation(e, "The {collection} collection for tenant {tenantId} does exist and cannot be deleted.", collectionId, tenantId);
                    deletionRecord[$"{collection}Collection"] = true;
                }
                catch (Exception e)
                {
                    deletionRecord[$"{collection}Collection"] = false;
                    this.logger.LogInformation(e, "An error occurred while deleting the {collection} collection for tenant {tenantId}", collectionId, tenantId);
                }

                try
                {
                    // now that we have the collection Id, delete the key from app config
                    await this.appConfigClient.DeleteKeyAsync(collectionAppConfigKey);
                }
                catch (Exception e)
                {
                    this.logger.LogInformation(e, "Unable to delete {collectionKey} from App Config", collectionAppConfigKey, tenantId);
                }
            }

            foreach (string blobContainer in this.tenantBlobContainers)
            {
                string containerName       = $"{tenantId}{blobContainer}";
                string deletionRecordValue = $"{containerName}BlobContainer";
                try
                {
                    await this.blobStorageClient.DeleteBlobContainerAsync(containerName);

                    deletionRecord[deletionRecordValue] = true;
                }
                catch (ResourceNotFoundException e)
                {
                    this.logger.LogInformation(e, $"Unable to delete blob container {containerName} for tenant {tenantId}. The blob container does not exist.");
                    deletionRecord[deletionRecordValue] = true;
                }
                catch (Exception e)
                {
                    this.logger.LogInformation(e, $"Unable to delete blob container {containerName} for tenant {tenantId}.");
                    deletionRecord[deletionRecordValue] = false;
                }
            }

            return(new DeleteTenantModel(tenantId, deletionRecord, ensureFullyDeployed));
        }
        public async Task <CreateTenantModel> CreateTenantAsync(string tenantId, string userId, string createdBy)
        {
            /* Creates a new tenant */
            string iotHubName = this.FormatResourceName(this.iotHubNameFormat, tenantId);
            string dpsName    = this.FormatResourceName(this.dpsNameFormat, tenantId);

            // Create a new tenant and save it to table storage
            var tenant = new TenantModel(tenantId);

            tenant.IotHubName = iotHubName;
            tenant.DpsName    = dpsName;
            await this.tableStorageClient.InsertAsync <TenantModel>(TenantTableId, tenant);

            // Give the requesting user an admin role to the new tenant
            try
            {
                await this.identityGatewayClient.AddTenantForUserAsync(userId, tenantId, CreatedRole, null, createdBy);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to add user to tenant.", e);
            }

            // Update the userSettings table with the lastUsedTenant if there isn't already a lastUsedTenant
            IdentityGatewayApiSettingModel userSettings = null;

            try
            {
                userSettings = await this.identityGatewayClient.GetSettingsForUserAsync(userId, LastUsedSettingKey);
            }
            catch (Exception e)
            {
                throw new Exception("Could not access user settings for LastUsedTenant.", e);
            }

            if (userSettings == null)
            {
                // Set the last used tenant to be this new tenant
                try
                {
                    await this.identityGatewayClient.AddSettingsForUserAsync(userId, LastUsedSettingKey, tenantId);
                }
                catch (Exception e)
                {
                    throw new Exception("Could not set user settings for LastUsedTenant.", e);
                }
            }

            // Add all the system admins as admin users to this system
            try
            {
                IdentityGatewayApiListModel systemAdmins = await this.identityGatewayClient.GetAllSystemAdminsAsync();

                foreach (var systemAdmin in systemAdmins.Models)
                {
                    if (systemAdmin.UserId != userId)
                    {
                        await this.identityGatewayClient.AddTenantForUserAsync(systemAdmin.UserId, tenantId, CreatedRole, systemAdmin.Name, createdBy);
                    }
                }
            }
            catch (Exception e)
            {
                throw new Exception("Could not insert system admins as users.", e);
            }

            // Write tenant info cosmos db collection name to app config
            try
            {
                foreach (string collection in this.tenantCollections.Keys)
                {
                    string collectionKey = string.Format(this.appConfigCollectionKeyFormat, tenantId, collection);
                    string collectionId  = $"{collection}-{tenantId}";
                    await this.appConfigClient.SetValueAsync(collectionKey, collectionId);
                }
            }
            catch (Exception e)
            {
                // In order for a complete tenant creation, all app config keys must be created. throw an error if not
                throw new Exception($"Unable to add required collection ids to App Config for tenant {tenantId}", e);
            }

            try
            {
                await this.deviceGroupClient.CreateDefaultDeviceGroupAsync(tenantId);
            }
            catch (Exception e)
            {
                throw new Exception("Unable to create the default device group for the new tenant.", e);
            }

            string grafanaTaskName = this.FormatResourceName(this.grafanaNameFormat, tenantId);

            if (string.Equals(this.config.DeviceTelemetryService.Messages.TelemetryStorageType, TelemetryStorageTypeConstants.Ade, StringComparison.OrdinalIgnoreCase))
            {
                // trigger grafana dashboard
                try
                {
                    await this.tableStorageClient.InsertAsync(TenantOperationTable, new TenantOperationModel(tenantId, TenantOperation.GrafanaDashboardCreation, grafanaTaskName));
                }
                catch (Exception e)
                {
                    this.logger.LogInformation(e, "Unable to create grafana dashboard for tenant {tenantId}", tenantId);
                }
            }

            return(new CreateTenantModel(tenantId));
        }