csharp/Azure/iotedge/edge-util/test/Microsoft.Azure.Devices.Edge.Util.Test/SharedAccessSignatureTest.cs

SharedAccessSignatureTest.cs
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Azure.Devices.Edge.Util.Test
{
    using System;
    using System.Net;
    using System.Threading;
    using Microsoft.Azure.Devices.Edge.Util.Test.Common;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Linq;
    using Xunit;

    public clast SharedAccessKeySignatureTest
    {
        const string IOT_HUB_NAME = "TestHub";
        const string MISSING_SHAREDACCESSSIGNATURE_RAW_TOKEN = "\nsig=test&se=0&sr=test";
        const string EXPIRED_RAW_TOKEN = "SharedAccessSignature\nsig=test&se=0&sr=test";
        const string BAD_TOKEN = "test";
        const string BAD_TOKEN_MISSING_SIGNATURE = "SharedAccessSignature\nse=0&sr=test";
        const string BAD_TOKEN_MISSING_EXPIRY = "SharedAccessSignature\nsig=test&sr=test";
        const string BAD_TOKEN_MISSING_ENCODING_AUDIENCE = "SharedAccessSignature\nsig=test&se=0;";
        const string WHITE_SPACE = "  ";

        [Fact]
        [Unit]
        public void IsExpiredTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetAlmostExpiredToken());

            Thread.Sleep(5 * 1000);

            astert.True(sas.IsExpired());

            sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken());

            astert.False(sas.IsExpired());
        }

        [Fact]
        [Unit]
        public void ParseThrowsWithNullIotHubNameTest() => astert.Throws(() => SharedAccessSignature.Parse(null, EXPIRED_RAW_TOKEN));

        [Fact]
        [Unit]
        public void ParseThrowsWithEmptyIotHubNameTest() => astert.Throws(() => SharedAccessSignature.Parse(string.Empty, EXPIRED_RAW_TOKEN));

        [Fact]
        [Unit]
        public void ParseThrowsWithWhiteSpaceIotHubNameTest() => astert.Throws(() => SharedAccessSignature.Parse(WHITE_SPACE, EXPIRED_RAW_TOKEN));

        [Fact]
        [Unit]
        public void ParseThrowsWithNullRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, null));

        [Fact]
        [Unit]
        public void ParseThrowsWithEmptyRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, string.Empty));

        [Fact]
        [Unit]
        public void ParseThrowsWithWhiteSpaceRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, WHITE_SPACE));

        [Fact]
        [Unit]
        public void ParseThrowsWithBadRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, BAD_TOKEN));

        [Fact]
        [Unit]
        public void ParseThrowsWithSignatureMissingFromRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, BAD_TOKEN_MISSING_SIGNATURE));

        [Fact]
        [Unit]
        public void ParseThrowsWithExpiryMissingFromRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, BAD_TOKEN_MISSING_EXPIRY));

        [Fact]
        [Unit]
        public void ParseThrowsWithsrMissingFromRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, BAD_TOKEN_MISSING_ENCODING_AUDIENCE));

        [Fact]
        [Unit]
        public void ParseThrowsWithSharedAccessSignatureMissingFromRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, MISSING_SHAREDACCESSSIGNATURE_RAW_TOKEN));

        [Fact]
        [Unit]
        public void ParseThrowsWithExpiredRawTokenTest() => astert.Throws(() => SharedAccessSignature.Parse(IOT_HUB_NAME, EXPIRED_RAW_TOKEN));

        [Fact]
        [Unit]
        public void ParseDoesNotThrowWithNonExpiredRawTokenTest() => this.astertNoThrow(() => SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken()));

        [Fact]
        [Unit]
        public void AuthenticateThrowsWhenRuleIsNullTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken());

            astert.Throws(() => sas.Authenticate(null));
        }

        [Fact]
        [Unit]
        public void AuthenticateThrowsIsExpiredTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetAlmostExpiredToken());

            var rule = new SharedAccessSignatureAuthorizationRule();

            Thread.Sleep(5 * 1000);

            astert.Throws(() => sas.Authenticate(rule));
        }

        [Fact]
        [Unit]
        public void AuthenticateThrowsInvalidSASTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken());

            var rule = new SharedAccessSignatureAuthorizationRule();

            astert.Throws(() => sas.Authenticate(rule));
        }

        [Fact]
        [Unit]
        public void AuthenticateSucceedsWhenValidSASWithPrimaryKeyTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken());

            var json = new JObject
            {
                ["keyName"] = "test",
                ["primaryKey"] = "test",
                ["secondaryKey"] = "YmFk"
            };

            var rule = JsonConvert.DeserializeObject(json.ToString());

            this.astertNoThrow(() => sas.Authenticate(rule));
        }

        [Fact]
        [Unit]
        public void AuthenticateSucceedsWhenValidSASWithSecondaryKeyTest()
        {
            var sas = SharedAccessSignature.Parse(IOT_HUB_NAME, this.GetNonExpiredToken());

            var json = new JObject
            {
                ["keyName"] = "test",
                ["primaryKey"] = "YmFk",
                ["secondaryKey"] = "test"
            };

            var rule = JsonConvert.DeserializeObject(json.ToString());

            this.astertNoThrow(() => sas.Authenticate(rule));
        }

        private string GetNonExpiredToken()
        {
            // Generate a valid looking sas token
            var expiry = this.GetExpiry(10).ToString();
            var sr = "test";

            var sig = SharedAccessSignature.ComputeSignature(Convert.FromBase64String("test"), sr, expiry);
            sig = WebUtility.UrlEncode(sig);

            return "SharedAccessSignature\nsig=" + sig + "&se=" + expiry + "&sr=" + sr;
        }

        private string GetAlmostExpiredToken() => "SharedAccessSignature\nsig=test&se=" + this.GetExpiry(2) + "&sr=test";

        private void astertNoThrow(Func test) => astert.Null(Record.Exception(test));

        private void astertNoThrow(Action test) => astert.Null(Record.Exception(test));

        private int GetExpiry(int secondsInFuture) => (int)Math.Floor((DateTime.UtcNow.AddSeconds(secondsInFuture).Subtract(SharedAccessSignatureConstants.MaxClockSkew) - SharedAccessSignatureConstants.EpochTime).TotalSeconds);
    }
}