Ejemplo n.º 1
0
        public async Task <bool> AuthenticateAsync(string id, Option <string> modelId, Option <string> authChain)
        {
            try
            {
                // See if we received direct credentials for the target identity
                bool hasToken = this.clientTokens.TryGetValue(id, out ClientToken clientToken);

                // Otherwise, this could be a child Edge acting OnBehalfOf of the target,
                // in which case we would have the actor's credentials instead
                if (!hasToken)
                {
                    Option <string> actorDeviceId = AuthChainHelpers.GetActorDeviceId(authChain);
                    hasToken = actorDeviceId.Map(actor => this.clientTokens.TryGetValue($"{actor}/{CoreConstants.EdgeHubModuleId}", out clientToken)).GetOrElse(false);
                }

                if (hasToken)
                {
                    // Lock the auth-state check and the auth call to avoid
                    // redundant calls into the authenticator
                    using (await this.identitySyncLock.LockAsync())
                    {
                        if (this.authenticatedClients.ContainsKey(id))
                        {
                            // We've previously authenticated this client already
                            return(true);
                        }
                        else
                        {
                            IClientCredentials clientCredentials = this.clientCredentialsFactory.GetWithSasToken(
                                clientToken.DeviceId,
                                clientToken.ModuleId.OrDefault(),
                                string.Empty,
                                clientToken.Token,
                                true,
                                modelId,
                                authChain);

                            if (await this.authenticator.AuthenticateAsync(clientCredentials))
                            {
                                // Authentication success, add an entry for the authenticated
                                // client identity, the value in the dictionary doesn't matter
                                // as we're effectively using it as a thread-safe HashSet
                                this.authenticatedClients[id] = default(byte);

                                // No need to add the new credentials to the cache, as the
                                // authenticator already implicitly handles it
                                return(true);
                            }
                        }
                    }
                }

                return(false);
            }
            catch (Exception e)
            {
                Events.ErrorAuthenticatingIdentity(id, e);
                return(false);
            }
        }
Ejemplo n.º 2
0
        async Task <bool> AuthorizeActorAsync(IDeviceScopeIdentitiesCache identitiesCache, string actorDeviceId, string actorModuleId, string targetId)
        {
            if (actorModuleId != Constants.EdgeHubModuleId)
            {
                // Only child EdgeHubs are allowed to act OnBehalfOf of devices/modules.
                Events.AuthFail_BadActor(actorDeviceId, actorModuleId, targetId);
                return(false);
            }

            // Actor device is claiming to be our child, and that the target device is its child.
            // So we should have an authchain already cached for the target device.
            Option <string> targetAuthChainOption = await identitiesCache.GetAuthChain(targetId);

            if (!targetAuthChainOption.HasValue)
            {
                Events.AuthFail_NoAuthChain(targetId);
                return(false);
            }

            // Validate the target auth-chain
            string targetAuthChain = targetAuthChainOption.Expect(() => new InvalidOperationException());

            if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, targetId, targetAuthChain))
            {
                Events.AuthFail_InvalidAuthChain(actorDeviceId, targetId, targetAuthChain);
                return(false);
            }

            return(true);
        }
Ejemplo n.º 3
0
        internal static async Task <EdgeHubScopeResult> HandleDevicesAndModulesInTargetDeviceScopeAsync(string actorDeviceId, string actorModuleId, NestedScopeRequest request, IDeviceScopeIdentitiesCache identitiesCache)
        {
            Events.ReceivedScopeRequest(actorDeviceId, actorModuleId, request);

            if (!AuthChainHelpers.TryGetTargetDeviceId(request.AuthChain, out string targetDeviceId))
            {
                return(new EdgeHubScopeResultError(HttpStatusCode.BadRequest, Events.InvalidRequestAuthchain(request.AuthChain)));
            }

            // Get the children of the target device and the target device itself;
            Option <string> authChainToTarget = await identitiesCache.GetAuthChain(targetDeviceId);

            (bool validationResult, string errorMsg) = ValidateAuthChainForRequestor(actorDeviceId, targetDeviceId, authChainToTarget);
            if (!validationResult)
            {
                return(new EdgeHubScopeResultError(HttpStatusCode.Unauthorized, errorMsg));
            }

            IList <ServiceIdentity> identities = await identitiesCache.GetDevicesAndModulesInTargetScopeAsync(targetDeviceId);

            Option <ServiceIdentity> targetDevice = await identitiesCache.GetServiceIdentity(targetDeviceId);

            targetDevice.ForEach(d => identities.Add(d));

            // Construct the result from the identities
            Events.SendingScopeResult(targetDeviceId, identities);
            return(MakeResultFromIdentities(identities));
        }
Ejemplo n.º 4
0
        public void TryGetTargetDeviceId_Success(string authChain, bool expected, string expectedTargetDeviceId)
        {
            bool actual = AuthChainHelpers.TryGetTargetDeviceId(authChain, out string actualTargetDeviceId);

            Assert.Equal(expected, actual);
            if (actual)
            {
                Assert.Equal(expectedTargetDeviceId, actualTargetDeviceId);
            }
        }
Ejemplo n.º 5
0
 internal bool ValidateAuthChain(string actorDeviceId, string targetId, string authChain)
 {
     if (AuthChainHelpers.ValidateAuthChain(actorDeviceId, targetId, authChain))
     {
         return(true);
     }
     else
     {
         Events.InvalidAuthChain(targetId, authChain);
         return(false);
     }
 }
Ejemplo n.º 6
0
        public void ValidateChainTest()
        {
            // Correct case
            Assert.True(AuthChainHelpers.ValidateAuthChain("edge1", "leaf1", "leaf1;edge1;edgeRoot"));

            // Unauthorized actor
            Assert.False(AuthChainHelpers.ValidateAuthChain("edge1", "leaf1", "leaf1;edge2;edgeRoot"));

            // Bad target
            Assert.False(AuthChainHelpers.ValidateAuthChain("edge1", "leaf1", "leaf2;edge1;edgeRoot"));

            // Invalid format
            Assert.False(AuthChainHelpers.ValidateAuthChain("edge1", "leaf1", ";"));
        }
Ejemplo n.º 7
0
        public async Task GetDevicesAndModulesInTargetDeviceScopeAsync([FromRoute] string actorDeviceId, [FromRoute] string actorModuleId, [FromBody] NestedScopeRequest request)
        {
            actorDeviceId = WebUtility.UrlDecode(Preconditions.CheckNonWhiteSpace(actorDeviceId, nameof(actorDeviceId)));
            actorModuleId = WebUtility.UrlDecode(Preconditions.CheckNonWhiteSpace(actorModuleId, nameof(actorModuleId)));
            Preconditions.CheckNonWhiteSpace(request.AuthChain, nameof(request.AuthChain));

            if (actorModuleId != Constants.EdgeHubModuleId)
            {
                // Only child EdgeHubs are allowed to act OnBehalfOf of devices/modules.
                var result = new EdgeHubScopeResultError(HttpStatusCode.Unauthorized, Events.UnauthorizedActor(actorDeviceId, actorModuleId));
                await this.SendResponse(result.Status, JsonConvert.SerializeObject(result));
            }

            string authChain = request.AuthChain;

            string[] ids = AuthChainHelpers.GetAuthChainIds(authChain);
            if (ids.Length == 1)
            {
                // A child EdgeHub can use its module credentials to calls upstream
                // OnBehalfOf its device identity, so the auth-chain would just have
                // one element denoting the target device scope but no actor.
                // However, the auth stack requires an actor to be specified for OnBehalfOf
                // connections, so we manually add the actor to the auth-chain for this
                // special case.
                authChain = $"{ids[0]}/{Constants.EdgeHubModuleId};{ids[0]}";
            }

            IHttpRequestAuthenticator authenticator = await this.authenticatorGetter;
            HttpAuthResult            authResult    = await authenticator.AuthenticateAsync(actorDeviceId, Option.Some(actorModuleId), Option.Some(authChain), this.HttpContext);

            if (authResult.Authenticated)
            {
                EdgeHubScopeResult reqResult = await this.HandleDevicesAndModulesInTargetDeviceScopeAsync(actorDeviceId, actorModuleId, request);

                await this.SendResponse(reqResult.Status, JsonConvert.SerializeObject(reqResult));
            }
            else
            {
                var result = new EdgeHubScopeResultError(HttpStatusCode.Unauthorized, authResult.ErrorMessage);
                await this.SendResponse(result.Status, JsonConvert.SerializeObject(result));
            }
        }
Ejemplo n.º 8
0
        async Task <EdgeHubScopeResult> HandleDevicesAndModulesInTargetDeviceScopeAsync(string actorDeviceId, string actorModuleId, NestedScopeRequest request)
        {
            Events.ReceivedScopeRequest(actorDeviceId, actorModuleId, request);

            if (!AuthChainHelpers.TryGetTargetDeviceId(request.AuthChain, out string targetDeviceId))
            {
                return(new EdgeHubScopeResultError(HttpStatusCode.BadRequest, Events.InvalidRequestAuthchain(request.AuthChain)));
            }

            // Get the children of the target device and the target device itself;
            IEdgeHub edgeHub = await this.edgeHubGetter;
            IDeviceScopeIdentitiesCache identitiesCache = edgeHub.GetDeviceScopeIdentitiesCache();
            IList <ServiceIdentity>     identities      = await identitiesCache.GetDevicesAndModulesInTargetScopeAsync(targetDeviceId);

            Option <ServiceIdentity> targetDevice = await identitiesCache.GetServiceIdentity(targetDeviceId);

            targetDevice.ForEach(d => identities.Add(d));

            // Construct the result from the identities
            Events.SendingScopeResult(targetDeviceId, identities);
            return(MakeResultFromIdentities(identities));
        }
Ejemplo n.º 9
0
        internal static async Task <Try <string> > AuthorizeOnBehalfOf(
            string actorDeviceId,
            string authChain,
            string source,
            HttpContext httpContext,
            IEdgeHub edgeHub,
            IHttpRequestAuthenticator authenticator)
        {
            if (!AuthChainHelpers.TryGetTargetDeviceId(authChain, out string targetDeviceId))
            {
                Events.InvalidRequestAuthChain(source, authChain);
                return(Try <string> .Failure(new ValidationException(HttpStatusCode.BadRequest, FormatErrorResponseMessage($"Invalid request auth chain {authChain}."))));
            }

            if (!await AuthenticateAsync(actorDeviceId, Option.Some(Constants.EdgeHubModuleId), Option.Some(authChain), httpContext, authenticator))
            {
                return(Try <string> .Failure(new ValidationException(HttpStatusCode.Unauthorized)));
            }

            IDeviceScopeIdentitiesCache identitiesCache = edgeHub.GetDeviceScopeIdentitiesCache();
            Option <string>             targetAuthChain = await identitiesCache.GetAuthChain(targetDeviceId);

            return(targetAuthChain.Match(
                       ac =>
            {
                if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, targetDeviceId, ac))
                {
                    Events.AuthorizationFail_InvalidAuthChain(actorDeviceId, targetDeviceId, ac);
                    return Try <string> .Failure(new ValidationException(HttpStatusCode.Unauthorized));
                }

                return ac;
            },
                       () =>
            {
                Events.AuthorizationFail_NoAuthChain(targetDeviceId);
                return Try <string> .Failure(new ValidationException(HttpStatusCode.Unauthorized));
            }));
        }
Ejemplo n.º 10
0
        public async Task <IDeviceIdentity> GetAsync(string clientId, string username, string password, EndPoint clientAddress)
        {
            try
            {
                Preconditions.CheckNonWhiteSpace(username, nameof(username));
                Preconditions.CheckNonWhiteSpace(clientId, nameof(clientId));

                ClientInfo clientInfo = this.usernameParser.Parse(username);
                clientInfo.ModelId.ForEach(async m => await this.metadataStore.SetModelId(clientInfo.DeviceId, m));
                IClientCredentials          deviceCredentials = null;
                Option <IClientCredentials> actorCredentials  = Option.None <IClientCredentials>();

                if (!string.IsNullOrEmpty(password))
                {
                    deviceCredentials = this.clientCredentialsFactory.GetWithSasToken(clientInfo.DeviceId, clientInfo.ModuleId, clientInfo.DeviceClientType, password, false, clientInfo.ModelId, clientInfo.AuthChain);

                    // For OnBehalfOf connections, we'll get the token for the actor EdgeHub instead
                    // of the actual leaf/module, so we need to construct the credentials accordingly
                    Option <string> actorDeviceIdOption = AuthChainHelpers.GetActorDeviceId(clientInfo.AuthChain);
                    actorCredentials = actorDeviceIdOption.Map(actorDeviceId =>
                                                               this.clientCredentialsFactory.GetWithSasToken(
                                                                   actorDeviceId,
                                                                   Microsoft.Azure.Devices.Edge.Hub.Core.Constants.EdgeHubModuleId,
                                                                   clientInfo.DeviceClientType,
                                                                   password,
                                                                   false,
                                                                   clientInfo.ModelId,
                                                                   clientInfo.AuthChain));
                }
                else if (this.remoteCertificate.HasValue)
                {
                    if (!this.clientCertAuthAllowed)
                    {
                        Events.CertAuthNotEnabled(clientInfo.DeviceId, clientInfo.ModuleId);
                        return(UnauthenticatedDeviceIdentity.Instance);
                    }

                    this.remoteCertificate.ForEach(
                        cert =>
                    {
                        deviceCredentials = this.clientCredentialsFactory.GetWithX509Cert(
                            clientInfo.DeviceId,
                            clientInfo.ModuleId,
                            clientInfo.DeviceClientType,
                            cert,
                            this.remoteCertificateChain,
                            clientInfo.ModelId,
                            Option.None <string>());
                    });
                }
                else
                {
                    Events.AuthNotFound(clientInfo.DeviceId, clientInfo.ModuleId);
                    return(UnauthenticatedDeviceIdentity.Instance);
                }

                if (deviceCredentials == null ||
                    !clientId.Equals(deviceCredentials.Identity.Id, StringComparison.Ordinal) ||
                    !await this.authenticator.AuthenticateAsync(actorCredentials.GetOrElse(deviceCredentials)))
                {
                    Events.Error(clientId, username);
                    return(UnauthenticatedDeviceIdentity.Instance);
                }

                await this.metadataStore.SetMetadata(deviceCredentials.Identity.Id, clientInfo.DeviceClientType, clientInfo.ModelId);

                Events.Success(clientId, username);
                return(new ProtocolGatewayIdentity(deviceCredentials, clientInfo.ModelId));
            }
            catch (Exception ex)
            {
                Events.ErrorCreatingIdentity(ex);
                throw;
            }
        }
Ejemplo n.º 11
0
 public void GetAuthChainIds_Fail(string authChain)
 {
     Assert.Throws <ArgumentException>(() => AuthChainHelpers.GetAuthChainIds(authChain));
 }
Ejemplo n.º 12
0
        public void GetAuthChainIds_Success()
        {
            Assert.Equal(new[] { "device1/$edgeHub", "device1", "device2" }, AuthChainHelpers.GetAuthChainIds("device1/$edgeHub;device1;device2"));

            Assert.Equal(new[] { "longdevicename" }, AuthChainHelpers.GetAuthChainIds("longdevicename"));
        }
Ejemplo n.º 13
0
        public async Task DeleteModuleOnBehalfOfAsync(
            [FromRoute] string actorDeviceId,
            [FromBody] DeleteModuleOnBehalfOfData requestData)
        {
            try
            {
                Events.ReceivedOnBehalfOfRequest(nameof(this.DeleteModuleOnBehalfOfAsync), actorDeviceId, Events.GetAdditionalInfo(requestData));

                try
                {
                    Preconditions.CheckNonWhiteSpace(actorDeviceId, nameof(actorDeviceId));
                    Preconditions.CheckNotNull(requestData, nameof(requestData));
                    Preconditions.CheckNonWhiteSpace(requestData.AuthChain, nameof(requestData.AuthChain));
                    Preconditions.CheckNonWhiteSpace(requestData.ModuleId, nameof(requestData.ModuleId));
                }
                catch (Exception ex)
                {
                    Events.BadRequest(nameof(this.DeleteModuleOnBehalfOfAsync), ex.Message);
                    await this.SendResponseAsync(HttpStatusCode.BadRequest, FormatErrorResponseMessage(ex.ToString()));

                    return;
                }

                actorDeviceId = WebUtility.UrlDecode(actorDeviceId);

                if (!AuthChainHelpers.TryGetTargetDeviceId(requestData.AuthChain, out string targetDeviceId))
                {
                    Events.InvalidRequestAuthChain(nameof(this.DeleteModuleOnBehalfOfAsync), requestData.AuthChain);
                    await this.SendResponseAsync(HttpStatusCode.BadRequest, FormatErrorResponseMessage($"Invalid request auth chain {requestData.AuthChain}."));

                    return;
                }

                if (!await this.AuthenticateAsync(actorDeviceId, Option.Some(Constants.EdgeHubModuleId), Option.Some(requestData.AuthChain)))
                {
                    await this.SendResponseAsync(HttpStatusCode.Unauthorized);

                    return;
                }

                IEdgeHub edgeHub = await this.edgeHubGetter;
                IDeviceScopeIdentitiesCache identitiesCache = edgeHub.GetDeviceScopeIdentitiesCache();
                Option <string>             targetAuthChain = await identitiesCache.GetAuthChain(targetDeviceId);

                if (!targetAuthChain.HasValue)
                {
                    Events.AuthorizationFail_NoAuthChain(targetDeviceId);
                    await this.SendResponseAsync(HttpStatusCode.Unauthorized);

                    return;
                }

                string edgeDeviceId          = edgeHub.GetEdgeDeviceId();
                RegistryApiHttpResult result = await this.apiClient.DeleteModuleAsync(
                    edgeDeviceId,
                    new DeleteModuleOnBehalfOfData($"{targetAuthChain.OrDefault()}", requestData.ModuleId));

                await this.SendResponseAsync(result.StatusCode, result.JsonContent);

                Events.CompleteRequest(nameof(this.DeleteModuleOnBehalfOfAsync), edgeDeviceId, targetAuthChain.OrDefault(), result);
            }
            catch (Exception ex)
            {
                Events.InternalServerError(nameof(this.DeleteModuleOnBehalfOfAsync), ex);
                await this.SendResponseAsync(HttpStatusCode.InternalServerError, FormatErrorResponseMessage(ex.ToString()));
            }
        }