/// <summary>
        /// Build an instance of <see cref="NodeEntitlements"/> from the information supplied on the
        /// command line by the user
        /// </summary>
        /// <returns>Either a usable (and completely valid) <see cref="NodeEntitlements"/> or a set
        /// of errors.</returns>
        private Errorable <NodeEntitlements> Build()
        {
            var entitlement = new NodeEntitlements();
            var errors      = new List <string>();

            ConfigureOptional(VirtualMachineId, url => entitlement.WithVirtualMachineId(url));
            Configure(NotBefore, notBefore => entitlement.FromInstant(notBefore));
            Configure(NotAfter, notAfter => entitlement.UntilInstant(notAfter));
            Configure(Audience, audience => entitlement.WithAudience(audience));
            Configure(Issuer, issuer => entitlement.WithIssuer(issuer));
            ConfigureAll(Addresses, address => entitlement.AddIpAddress(address));
            ConfigureAll(Applications, app => entitlement.AddApplication(app));

            if (errors.Any())
            {
                return(Errorable.Failure <NodeEntitlements>(errors));
            }

            return(Errorable.Success(entitlement));

            // <param name="readConfiguration">function to read the configuration value.</param>
            // <param name="applyConfiguration">function to modify our configuration with the value read.</param>
            void Configure <V>(Func <Errorable <V> > readConfiguration, Func <V, NodeEntitlements> applyConfiguration)
            {
                readConfiguration().Match(
                    whenSuccessful: value => entitlement = applyConfiguration(value),
                    whenFailure: e => errors.AddRange(e));
            }

            // <param name="readConfiguration">function to read the configuration value.</param>
            // <param name="applyConfiguration">function to modify our configuration with the value read.</param>
            void ConfigureOptional <V>(Func <Errorable <V> > readConfiguration, Func <V, NodeEntitlements> applyConfiguration)
                where V : class
Example #2
0
        /// <summary>
        /// Verify the provided software entitlement token
        /// </summary>
        /// <param name="tokenString">A software entitlement token to verify.</param>
        /// <param name="expectedAudience">The audience for whom the token should be intended.</param>
        /// <param name="expectedIssuer">The issuer who should have created the token.</param>
        /// <param name="application">The specific application id of the application </param>
        /// <param name="ipAddress">Address of the machine requesting token validation.</param>
        /// <returns>Either a software entitlement describing the approved entitlement, or errors
        /// explaining why it wasn't approved.</returns>
        public Errorable <NodeEntitlements> Verify(
            string tokenString,
            string expectedAudience,
            string expectedIssuer,
            string application,
            IPAddress ipAddress)
        {
            var validationParameters = new TokenValidationParameters
            {
                ValidateAudience         = true,
                ValidAudience            = expectedAudience,
                ValidateIssuer           = true,
                ValidIssuer              = expectedIssuer,
                ValidateLifetime         = true,
                RequireExpirationTime    = true,
                RequireSignedTokens      = SigningKey != null,
                ClockSkew                = TimeSpan.FromSeconds(60),
                IssuerSigningKey         = SigningKey,
                ValidateIssuerSigningKey = true,
                TokenDecryptionKey       = EncryptionKey
            };

            try
            {
                var handler   = new JwtSecurityTokenHandler();
                var principal = handler.ValidateToken(tokenString, validationParameters, out var token);

                if (!VerifyApplication(principal, application))
                {
                    return(ApplicationNotEntitledError(application));
                }

                if (!VerifyIpAddress(principal, ipAddress))
                {
                    return(MachineNotEntitledError(ipAddress));
                }

                var entitlementIdClaim = principal.FindFirst(Claims.EntitlementId);
                if (entitlementIdClaim == null)
                {
                    return(IdentifierNotPresentError());
                }

                var result = new NodeEntitlements()
                             .FromInstant(new DateTimeOffset(token.ValidFrom))
                             .UntilInstant(new DateTimeOffset(token.ValidTo))
                             .AddApplication(application)
                             .WithIdentifier(entitlementIdClaim.Value)
                             .AddIpAddress(ipAddress);

                var virtualMachineIdClaim = principal.FindFirst(Claims.VirtualMachineId);
                if (virtualMachineIdClaim != null)
                {
                    result = result.WithVirtualMachineId(virtualMachineIdClaim.Value);
                }

                return(Errorable.Success(result));
            }
            catch (SecurityTokenNotYetValidException exception)
            {
                return(TokenNotYetValidError(exception.NotBefore));
            }
            catch (SecurityTokenExpiredException exception)
            {
                return(TokenExpiredError(exception.Expires));
            }
            catch (SecurityTokenException exception)
            {
                return(InvalidTokenError(exception.Message));
            }
        }