예제 #1
0
        async Task <(bool isAuthenticated, bool serviceIdentityFound)> AuthenticateWithAuthChain(T credentials, string actorDeviceId, bool syncServiceIdentity)
        {
            // The auth target is the first element of the authchain
            Option <string> authTargetOption = AuthChainHelpers.GetAuthTarget(credentials.AuthChain);
            string          authTarget       = authTargetOption.Expect(() => new InvalidOperationException("Credentials should always have a valid auth-chain for OnBehalfOf authentication"));

            // For nested Edge, we need to check that we have
            // a valid authchain for the target identity
            Option <string> authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget);

            if (!authChain.HasValue)
            {
                Events.NoAuthChain(authTarget);
                return(false, false);
            }

            // Check that the actor is authorized to connect OnBehalfOf of the target
            if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, authTarget, authChain.OrDefault()))
            {
                // We found the target identity in our cache, but can't proceed with auth
                Events.UnauthorizedAuthChain(actorDeviceId, authTarget, authChain.OrDefault());
                return(false, true);
            }

            // Check credentials against the acting EdgeHub
            string actorEdgeHubId = actorDeviceId + $"/{Constants.EdgeHubModuleId}";

            return(await this.AuthenticateWithServiceIdentity(credentials, actorEdgeHubId, syncServiceIdentity));
        }
        public async Task RefreshAuthChain(string authChain)
        {
            Preconditions.CheckNonWhiteSpace(authChain, nameof(authChain));

            // Refresh each element in the auth-chain
            Events.RefreshingAuthChain(authChain);
            string[] ids = AuthChainHelpers.GetAuthChainIds(authChain);
            await this.RefreshServiceIdentities(ids);
        }
예제 #3
0
        async Task <(bool isAuthenticated, bool shouldFallback)> AuthenticateInternalAsync(T tCredentials, bool reauthenticating)
        {
            try
            {
                if (!this.AreInputCredentialsValid(tCredentials))
                {
                    Events.InputCredentialsNotValid(tCredentials.Identity);
                    return(false, false);
                }

                bool syncServiceIdentity = this.syncServiceIdentityOnFailure && !reauthenticating;
                bool isAuthenticated     = false;
                bool valueFound          = false;

                // Try to get the actor from the client auth chain, this will only be valid for OnBehalfOf authentication
                Option <string> onBehalfOfActorDeviceId = AuthChainHelpers.GetActorDeviceId(tCredentials.AuthChain);

                if (this.nestedEdgeEnabled &&
                    onBehalfOfActorDeviceId.HasValue)
                {
                    // OnBehalfOf means an EdgeHub is trying to use its own auth to
                    // connect as a child device or module. So if acting EdgeHub is
                    // different than the target identity, then we know we're
                    // processing an OnBehalfOf authentication.
                    (isAuthenticated, valueFound) = await this.AuthenticateWithAuthChain(tCredentials, onBehalfOfActorDeviceId.OrDefault(), syncServiceIdentity);
                }
                else
                {
                    // Default scenario where the credential is for the target identity
                    (isAuthenticated, valueFound) = await this.AuthenticateWithServiceIdentity(tCredentials, tCredentials.Identity.Id, syncServiceIdentity);

                    if (!isAuthenticated && this.allowDeviceAuthForModule && tCredentials.Identity is IModuleIdentity moduleIdentity)
                    {
                        // The module could have used the Device key to sign its token
                        Events.AuthenticatingWithDeviceIdentity(moduleIdentity);
                        (isAuthenticated, valueFound) = await this.AuthenticateWithServiceIdentity(tCredentials, moduleIdentity.DeviceId, syncServiceIdentity);
                    }
                }

                // In the return value, the first flag indicates if the authentication succeeded.
                // The second flag indicates whether the authenticator should fall back to the underlying authenticator. This is done if
                // the ServiceIdentity was not found (which means the device/module is not in scope).
                return(isAuthenticated, !valueFound);
            }
            catch (Exception e)
            {
                Events.ErrorAuthenticating(e, tCredentials, reauthenticating);
                return(false, true);
            }
        }
예제 #4
0
        async Task <(bool isAuthenticated, bool serviceIdentityFound)> AuthenticateWithAuthChain(T credentials, string actorDeviceId, bool syncServiceIdentity)
        {
            // The auth target is the first element of the authchain
            Option <string> authTargetOption = AuthChainHelpers.GetAuthTarget(credentials.AuthChain);
            string          authTarget       = authTargetOption.Expect(() => new InvalidOperationException("Credentials should always have a valid auth-chain for OnBehalfOf authentication"));

            // For nested Edge, we need to check that we have
            // a valid authchain for the target identity
            Option <string> authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget);

            if (!authChain.HasValue)
            {
                // The auth-target might be a new device that was recently added, and our
                // cache might not have it yet. Try refreshing the target identity to see
                // if we can get it from upstream.
                Events.NoAuthChainResyncing(authTarget, actorDeviceId);
                await this.deviceScopeIdentitiesCache.RefreshServiceIdentityOnBehalfOf(authTarget, actorDeviceId);

                authChain = await this.deviceScopeIdentitiesCache.GetAuthChain(authTarget);

                if (!authChain.HasValue)
                {
                    // Still don't have a valid auth-chain for the target, it must be
                    // out of scope, so we're done here
                    Events.NoAuthChain(authTarget);
                    return(false, false);
                }
            }

            // Check that the actor is authorized to connect OnBehalfOf of the target
            if (!AuthChainHelpers.ValidateAuthChain(actorDeviceId, authTarget, authChain.OrDefault()))
            {
                // We found the target identity in our cache, but can't proceed with auth
                Events.UnauthorizedAuthChain(actorDeviceId, authTarget, authChain.OrDefault());
                return(false, true);
            }

            // Check credentials against the acting EdgeHub, since we would have
            // already refreshed the target identity on failure, there's no need
            // to have AuthenticateWithServiceIdentity do it again.
            string actorEdgeHubId = actorDeviceId + $"/{Constants.EdgeHubModuleId}";

            return(await this.AuthenticateWithServiceIdentity(credentials, actorEdgeHubId, false));
        }
        public async Task Add(IClientCredentials clientCredentials)
        {
            if (clientCredentials is ITokenCredentials tokenCredentials)
            {
                string targetId = AuthChainHelpers.GetAuthTarget(tokenCredentials.AuthChain).GetOrElse(tokenCredentials.Identity.Id);

                try
                {
                    var    tokenCredentialsData   = new TokenCredentialsData(tokenCredentials.Token, tokenCredentials.IsUpdatable, tokenCredentials.AuthChain);
                    string tokenCredentialsString = JsonConvert.SerializeObject(tokenCredentialsData);
                    await this.encryptedStore.Put(targetId, tokenCredentialsString);

                    Events.Stored(clientCredentials.Identity.Id);
                }
                catch (Exception e)
                {
                    Events.ErrorStoring(e, clientCredentials.Identity.Id);
                }
            }
        }
예제 #6
0
        void UpdateCachedCredentials(IClientCredentials clientCredentials)
        {
            string targetId = AuthChainHelpers.GetAuthTarget(clientCredentials.AuthChain).GetOrElse(clientCredentials.Identity.Id);

            if (clientCredentials.AuthChain.HasValue)
            {
                // OnBehalfOf connection from a child Edge, there should always be an actor device
                string actorDeviceId = AuthChainHelpers.GetActorDeviceId(clientCredentials.AuthChain)
                                       .Expect(() => new ArgumentException($"Invalid auth-chain: {clientCredentials.AuthChain.OrDefault()}"));

                // Only EdgeHub can act OnBehalfOf another identity
                string actorId = $"{actorDeviceId}/{Constants.EdgeHubModuleId}";

                if (!this.onBehalfOfRelations.ContainsKey(actorId))
                {
                    // Create a new OnBehalfOf mapping for this actor Edge
                    this.onBehalfOfRelations[actorId] = new HashSet <string>();
                }

                // Update our books for the new relation
                this.onBehalfOfRelations[actorId].Add(targetId);
            }
            else
            {
                // If there's no auth-chain, then this could be a credential update
                // for a child EdgeHub acting OnBehalfOf other identities
                if (this.onBehalfOfRelations.TryGetValue(clientCredentials.Identity.Id, out HashSet <string> onBehalfOfClients))
                {
                    // Need to update all clients that the child EdgeHub is acting OnBehalfOf
                    foreach (string client in onBehalfOfClients)
                    {
                        IClientCredentials updatedCredentials = GetCredentialsWithOnBehalfOfUpdates(this.credentials[client], clientCredentials);
                        this.credentials[client] = updatedCredentials;
                    }
                }
            }

            // Update the client credentials
            this.credentials[targetId] = clientCredentials;
        }
예제 #7
0
        async Task <string> VerifyServiceIdentityAuthChainState(string id, bool refreshCachedIdentity = false)
        {
            if (refreshCachedIdentity)
            {
                Events.RefreshingServiceIdentity(id);

                var authChainTry = await this.serviceIdentityHierarchy.TryGetAuthChain(id);

                var onBehalfOfDeviceId = AuthChainHelpers.GetAuthParent(authChainTry.Ok());
                await this.RefreshServiceIdentityOnBehalfOf(id, onBehalfOfDeviceId.GetOrElse(this.edgeDeviceId));
            }

            string authChain = (await this.serviceIdentityHierarchy.TryGetAuthChain(id)).Value;

            Option <ServiceIdentity> serviceIdentity = await this.GetServiceIdentity(id);

            this.VerifyServiceIdentity(serviceIdentity.Expect(() =>
            {
                Events.VerifyServiceIdentityFailure(id, "Device is out of scope.");
                return(new DeviceInvalidStateException("Device is out of scope."));
            }));

            return(authChain);
        }