Пример #1
0
        public async Task AuthenticateAsync_WithToken_PerformsAuthentication_Using_ContainerEncryptionKey_If_WebSiteAuthEncryptionKey_Not_Available()
        {
            var containerEncryptionKeyBytes = TestHelpers.GenerateKeyBytes();

            var vars = new Dictionary <string, string>
            {
                { EnvironmentSettingNames.WebSiteAuthEncryptionKey, string.Empty },
                { EnvironmentSettingNames.ContainerEncryptionKey, TestHelpers.GenerateKeyHexString(containerEncryptionKeyBytes) }
            };

            using (var env = new TestScopedEnvironmentVariable(vars))
            {
                // Arrange
                DefaultHttpContext context = GetContext();

                string token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(2), containerEncryptionKeyBytes);
                context.Request.Headers.Add(ArmAuthenticationHandler.ArmTokenHeaderName, token);

                // Act
                AuthenticateResult result = await context.AuthenticateAsync();

                // Assert
                Assert.True(result.Succeeded);
                Assert.True(result.Principal.Identity.IsAuthenticated);
                Assert.True(result.Principal.HasClaim(SecurityConstants.AuthLevelClaimType, AuthorizationLevel.Admin.ToString()));
            }
        }
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app ownership using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> SetTriggersAsync(string content)
        {
            var token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            _logger.LogDebug($"SyncTriggers content: {content}");

            using (var request = BuildSetTriggersRequest())
            {
                // This has to start with Mozilla because the frontEnd checks for it.
                request.Headers.Add("User-Agent", "Mozilla/5.0");
                request.Headers.Add("x-ms-site-restricted-token", token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                var response = await _httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    _logger.LogDebug($"SyncTriggers call succeeded.");
                    return(true, null);
                }
                else
                {
                    string message = $"SyncTriggers call failed. StatusCode={response.StatusCode}";
                    _logger.LogDebug(message);
                    return(false, message);
                }
            }
        }
Пример #3
0
        public IActionResult GetAdminToken()
        {
            if (!_environment.IsLinuxContainerEnvironment())
            {
                return(BadRequest("Endpoint is only available when running in Linux Container"));
            }

            string requestHeaderToken = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            return(Ok(requestHeaderToken));
        }
Пример #4
0
        public void CreateTokenShouldCreateAValidToken()
        {
            var key       = TestHelpers.GenerateKeyBytes();
            var stringKey = TestHelpers.GenerateKeyHexString(key);
            var timeStamp = DateTime.UtcNow;

            Environment.SetEnvironmentVariable("WEBSITE_AUTH_ENCRYPTION_KEY", stringKey);

            var token     = SimpleWebTokenHelper.CreateToken(timeStamp);
            var decrypted = SimpleWebTokenHelper.Decrypt(key, token);

            Assert.Equal($"exp={timeStamp.Ticks}", decrypted);
        }
Пример #5
0
        public void Validate_Token_Uses_Website_Encryption_Key_If_Container_Encryption_Key_Not_Available()
        {
            var websiteAuthEncryptionKey       = TestHelpers.GenerateKeyBytes();
            var websiteAuthEncryptionStringKey = TestHelpers.GenerateKeyHexString(websiteAuthEncryptionKey);

            var timeStamp = DateTime.UtcNow.AddHours(1);

            using (new TestScopedEnvironmentVariable(SettingsKeys.ContainerEncryptionKey, string.Empty))
                using (new TestScopedEnvironmentVariable(SettingsKeys.AuthEncryptionKey, websiteAuthEncryptionStringKey))
                {
                    var token = SimpleWebTokenHelper.CreateToken(timeStamp, websiteAuthEncryptionKey);
                    Assert.True(SimpleWebTokenHelper.TryValidateToken(token, new SystemClock()));
                }
        }
Пример #6
0
        public void CreateTokenShouldCreateAValidToken()
        {
            var key       = TestHelpers.GenerateKeyBytes();
            var stringKey = TestHelpers.GenerateKeyHexString(key);
            var timeStamp = DateTime.UtcNow;

            using (new TestScopedEnvironmentVariable(SettingsKeys.AuthEncryptionKey, stringKey))
            {
                var token     = SimpleWebTokenHelper.CreateToken(timeStamp);
                var decrypted = SimpleWebTokenHelper.Decrypt(key, token);

                Assert.Equal($"exp={timeStamp.Ticks}", decrypted);
            }
        }
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app ownership using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> SetTriggersAsync(string content)
        {
            var token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            string sanitizedContentString = content;

            if (ArmCacheEnabled)
            {
                // sanitize the content before logging
                var sanitizedContent = JToken.Parse(content);
                if (sanitizedContent.Type == JTokenType.Object)
                {
                    ((JObject)sanitizedContent).Remove("secrets");
                    sanitizedContentString = sanitizedContent.ToString();
                }
            }

            using (var request = BuildSetTriggersRequest())
            {
                var requestId = Guid.NewGuid().ToString();
                request.Headers.Add(ScriptConstants.AntaresLogIdHeaderName, requestId);
                request.Headers.Add("User-Agent", ScriptConstants.FunctionsUserAgent);
                request.Headers.Add(ScriptConstants.SiteTokenHeaderName, token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                if (_environment.IsKubernetesManagedHosting())
                {
                    request.Headers.Add(ScriptConstants.KubernetesManagedAppName, _environment.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteName));
                    request.Headers.Add(ScriptConstants.KubernetesManagedAppNamespace, _environment.GetEnvironmentVariable(EnvironmentSettingNames.PodNamespace));
                }

                _logger.LogDebug($"Making SyncTriggers request (RequestId={requestId}, Uri={request.RequestUri.ToString()}, Content={sanitizedContentString}).");

                var response = await _httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    _logger.LogDebug("SyncTriggers call succeeded.");
                    return(true, null);
                }
                else
                {
                    string message = $"SyncTriggers call failed (StatusCode={response.StatusCode}).";
                    _logger.LogDebug(message);
                    return(false, message);
                }
            }
        }
Пример #8
0
        private HttpRequestMessage BuildRequest <TContent>(HttpMethod method, string path, TContent content)
        {
            var request = new HttpRequestMessage(method, _requestUri + path)
            {
                Content = new ObjectContent <TContent>(content, new JsonMediaTypeFormatter())
            };

            var token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            // add the required authentication headers
            request.Headers.Add(ContainerNameHeader, _containerName);
            request.Headers.Add(HostNameHeader, _hostNameProvider.Value);
            request.Headers.Add(ScriptConstants.SiteTokenHeaderName, token);
            request.Headers.Add(StampNameHeader, _stampName);

            return(request);
        }
Пример #9
0
        public void Validate_Token_Uses_WebSiteAuthEncryptionKey_If_Available()
        {
            var containerEncryptionKey       = TestHelpers.GenerateKeyBytes();
            var containerEncryptionStringKey = TestHelpers.GenerateKeyHexString(containerEncryptionKey);

            var websiteAuthEncryptionKey       = TestHelpers.GenerateKeyBytes();
            var websiteAuthEncryptionStringKey = TestHelpers.GenerateKeyHexString(websiteAuthEncryptionKey);

            var timeStamp = DateTime.UtcNow.AddHours(1);

            Environment.SetEnvironmentVariable(EnvironmentSettingNames.ContainerEncryptionKey, containerEncryptionStringKey);
            Environment.SetEnvironmentVariable(EnvironmentSettingNames.WebSiteAuthEncryptionKey, websiteAuthEncryptionStringKey);

            var token = SimpleWebTokenHelper.CreateToken(timeStamp, websiteAuthEncryptionKey);

            Assert.True(SimpleWebTokenHelper.TryValidateToken(token, new SystemClock()));
        }
Пример #10
0
        public async Task AuthenticateAsync_WithExpiredToken_FailsAuthentication()
        {
            using (new TestScopedEnvironmentVariable(EnvironmentSettingNames.WebSiteAuthEncryptionKey, TestHelpers.GenerateKeyHexString()))
            {
                DefaultHttpContext context = GetContext();

                string token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(-20));
                context.Request.Headers.Add(ArmAuthenticationHandler.ArmTokenHeaderName, token);

                // Act
                AuthenticateResult result = await context.AuthenticateAsync();

                // Assert
                Assert.False(result.Succeeded);
                Assert.NotNull(result.Failure);
                Assert.Null(result.Principal);
            }
        }
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app owner ship using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> InternalSyncTriggers(IEnumerable <JObject> triggers)
        {
            var content = JsonConvert.SerializeObject(triggers);
            var token   = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            using (var request = BuildSyncTriggersRequest())
            {
                // This has to start with Mozilla because the frontEnd checks for it.
                request.Headers.Add("User-Agent", "Mozilla/5.0");
                request.Headers.Add("x-ms-site-restricted-token", token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                var response = await _client.SendAsync(request);

                return(response.IsSuccessStatusCode
                    ? (true, string.Empty)
                    : (false, $"Sync triggers failed with: {response.StatusCode}"));
            }
        }
Пример #12
0
        public async Task AuthenticateAsync_WithToken_PerformsAuthentication()
        {
            using (new TestScopedEnvironmentVariable(EnvironmentSettingNames.WebSiteAuthEncryptionKey, TestHelpers.GenerateKeyHexString()))
            {
                // Arrange
                DefaultHttpContext context = GetContext();

                string token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(2));
                context.Request.Headers.Add(ArmAuthenticationHandler.ArmTokenHeaderName, token);

                // Act
                AuthenticateResult result = await context.AuthenticateAsync();

                // Assert
                Assert.True(result.Succeeded);
                Assert.True(result.Principal.Identity.IsAuthenticated);
                Assert.True(result.Principal.HasClaim(SecurityConstants.AuthLevelClaimType, AuthorizationLevel.Admin.ToString()));
            }
        }
Пример #13
0
        public async Task AuthenticateAsync_WithInvalidToken_FailsAuthentication()
        {
            using (new TestScopedEnvironmentVariable(EnvironmentSettingNames.WebSiteAuthEncryptionKey, TestHelpers.GenerateKeyHexString()))
            {
                // Arrange
                DefaultHttpContext context = GetContext();

                string token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(2));
                token = token.Substring(0, token.Length - 5);
                context.Request.Headers.Add(ScriptConstants.SiteTokenHeaderName, token);

                // Act
                AuthenticateResult result = await context.AuthenticateAsync();

                // Assert
                Assert.False(result.Succeeded);
                Assert.NotNull(result.Failure);
                Assert.Null(result.Principal);
            }
        }
Пример #14
0
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app owner ship using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> InternalSyncTriggers(IEnumerable <JObject> triggers)
        {
            var content = JsonConvert.SerializeObject(triggers);
            var token   = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            var url = $"https://{Environment.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName)}/operations/settriggers";

            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                // This has to start with Mozilla because the frontEnd checks for it.
                request.Headers.Add("User-Agent", "Mozilla/5.0");
                request.Headers.Add("x-ms-site-restricted-token", token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                var response = await _client.SendAsync(request);

                return(response.IsSuccessStatusCode
                    ? (true, string.Empty)
                    : (false, $"Sync triggers failed with: {response.StatusCode}"));
            }
        }
Пример #15
0
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app ownership using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> SetTriggersAsync(string content)
        {
            var token = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            string sanitizedContentString = content;

            if (ArmCacheEnabled)
            {
                // sanitize the content before logging
                var sanitizedContent = JObject.Parse(content);
                sanitizedContent.Remove("secrets");
                sanitizedContentString = sanitizedContent.ToString();
            }

            using (var request = BuildSetTriggersRequest())
            {
                var requestId = Guid.NewGuid().ToString();
                request.Headers.Add(ScriptConstants.AntaresLogIdHeaderName, requestId);
                request.Headers.Add("User-Agent", ScriptConstants.FunctionsUserAgent);
                request.Headers.Add("x-ms-site-restricted-token", token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                _logger.LogDebug($"Making SyncTriggers request (RequestId={requestId}, Uri={request.RequestUri.ToString()}, Content={sanitizedContentString}).");

                var response = await _httpClient.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    _logger.LogDebug($"SyncTriggers call succeeded.");
                    return(true, null);
                }
                else
                {
                    string message = $"SyncTriggers call failed (StatusCode={response.StatusCode}).";
                    _logger.LogDebug(message);
                    return(false, message);
                }
            }
        }
Пример #16
0
        // This function will call POST https://{app}.azurewebsites.net/operation/settriggers with the content
        // of triggers. It'll verify app owner ship using a SWT token valid for 5 minutes. It should be plenty.
        private async Task <(bool, string)> InternalSyncTriggers(IEnumerable <JObject> triggers)
        {
            var content = JsonConvert.SerializeObject(triggers);
            var token   = SimpleWebTokenHelper.CreateToken(DateTime.UtcNow.AddMinutes(5));

            // This will be a problem for national clouds. However, Antares isn't injecting the
            // WEBSITE_HOSTNAME yet for linux apps. So until then will use this.
            var url = $"https://{Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME")}.azurewebsites.net/operations/settriggers";

            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                // This has to start with Mozilla because the frontEnd checks for it.
                request.Headers.Add("User-Agent", "Mozilla/5.0");
                request.Headers.Add("x-ms-site-restricted-token", token);
                request.Content = new StringContent(content, Encoding.UTF8, "application/json");

                var response = await _client.SendAsync(request);

                return(response.IsSuccessStatusCode
                    ? (true, string.Empty)
                    : (false, $"Sync triggers failed with: {response.StatusCode}"));
            }
        }