Ejemplo n.º 1
0
        public async Task <ServiceBinding> BindAsync(ServiceBindingContext context, ServiceBindingRequest request)
        {
            LogContext(_log, "Bind", context);
            LogRequest(_log, request);

            // Retrieve Azure Storage account.
            var storageAccounts = await _azureStorageProviderClient.GetStorageAccountsByTag("cf_service_instance_id", context.InstanceId);

            var nrStorageAccounts = storageAccounts.Count();

            if (nrStorageAccounts == 0)
            {
                var message = $"Could not find storage account with tag: cf_service_instance_id = {context.InstanceId}";
                _log.LogWarning(message);
                throw new ArgumentException(message, nameof(context));
            }

            if (nrStorageAccounts > 1)
            {
                var message = $"Found multiple storage accounts for tag: cf_service_instance_id = {context.InstanceId}";
                _log.LogError(message);
                throw new ArgumentException(message, nameof(context));
            }

            var storageAccount   = storageAccounts.Single();
            var storageAccountId = storageAccount.Id;

            // Must be in a separate method because Span is not allowed inside async methods.
            string GeneratePassword()
            {
                var randomNumberGenerator = new RNGCryptoServiceProvider();
                var randomNr = new Span <byte>(new byte[16]);

                randomNumberGenerator.GetBytes(randomNr);
                return(Convert.ToBase64String(randomNr));
            }

            // Create an Azure AD application.
            var clientSecret = GeneratePassword();
            var application  = new Application
            {
                DisplayName         = context.BindingId,
                IdentifierUris      = { $"https://{context.BindingId}" },
                PasswordCredentials =
                {
                    new PasswordCredential
                    {
                        StartDateTime = DateTimeOffset.UtcNow,
                        EndDateTime   = DateTimeOffset.UtcNow.AddYears(2),
                        KeyId         = Guid.NewGuid(),
                        SecretText    = clientSecret
                    }
                },
                SignInAudience = SignInAudience.AzureADMyOrg,
                Tags           = { $"cf_service_id:{request.ServiceId}", $"cf_plan_id:{request.PlanId}", $"cf_binding_id:{context.BindingId}" }
            };
            var createdApplication = await _msGraphClient.CreateApplication(application);

            // Create a service principal for the application in the same tenant.
            var servicePrincipal = new ServicePrincipal
            {
                AccountEnabled = true,
                AppId          = createdApplication.AppId,
                DisplayName    = createdApplication.DisplayName,
                Tags           = { $"cf_service_id:{request.ServiceId}", $"cf_plan_id:{request.PlanId}", $"cf_binding_id:{context.BindingId}" }
            };
            var createdServicePrincipal = await _msGraphClient.CreateServicePrincipal(servicePrincipal);

            var principalId = Guid.Parse(createdServicePrincipal.Id);

            // Assign service principal to roles Storage Blob Data Contributor and Storage Queue Data Contributor.
            var storageBlobDataContributorRoleId = Guid.Parse("ba92f5b4-2d11-453d-a403-e96b0029c9fe");

            await GrantPrincipalAccessToStorageAccount(storageAccountId, storageBlobDataContributorRoleId, principalId);

            var storageQueueDataContributorRoleId = Guid.Parse("974c5e8b-45b9-4653-ba55-5f855dd0fb88");

            await GrantPrincipalAccessToStorageAccount(storageAccountId, storageQueueDataContributorRoleId, principalId);

            // Get the access keys for the storage account.
            var storageAccountKeys = await _azureStorageClient.GetStorageAccountKeys(storageAccountId);

            return(new ServiceBinding
            {
                Credentials = JObject.FromObject(new StorageAccountCredentials
                {
                    Urls =
                    {
                        BlobStorageUrl  = $"https://{storageAccount.Name}.blob.core.windows.net",
                        QueueStorageUrl = $"https://{storageAccount.Name}.queue.core.windows.net",
                        TableStorageUrl = $"https://{storageAccount.Name}.table.core.windows.net",
                        FileStorageUrl  = $"https://{storageAccount.Name}.file.core.windows.net",
                    },
                    SharedKeys = storageAccountKeys
                                 .Select(key => new SharedKey
                    {
                        Name = key.KeyName,
                        Permissions = key.Permissions.ToString(),
                        Value = key.Value
                    })
                                 .ToArray(),
                    OAuthClientCredentials =
                    {
                        ClientId      = createdApplication.AppId,
                        ClientSecret  = clientSecret,
                        TokenEndpoint = $"https://login.microsoftonline.com/{_azureAuthOptions.TenantId}/oauth2/v2.0/token",
                        Scopes        = new[] { "https://management.core.windows.net/.default" },
                        GrantType     = "client_credentials"
                    }
                })
            });
        }