protected CanAccessDatabase GetDatabaseAccessValidationFunc(RavenServer.AuthenticateConnection authenticationStatus = null)
        {
            RavenServer.AuthenticateConnection feature;

            if (authenticationStatus != null)
            {
                feature = authenticationStatus;
            }
            else
            {
                feature = HttpContext.Features.Get <IHttpAuthenticationFeature>() as RavenServer.AuthenticateConnection;
            }

            var status = feature?.Status;

            switch (status)
            {
            case null:
            case RavenServer.AuthenticationStatus.None:
            case RavenServer.AuthenticationStatus.NoCertificateProvided:
            case RavenServer.AuthenticationStatus.UnfamiliarCertificate:
            case RavenServer.AuthenticationStatus.UnfamiliarIssuer:
            case RavenServer.AuthenticationStatus.Expired:
            case RavenServer.AuthenticationStatus.NotYetValid:
                if (Server.Configuration.Security.AuthenticationEnabled == false)
                {
                    return(null);
                }
                return((_, __) => false);    // deny everything

            case RavenServer.AuthenticationStatus.Operator:
            case RavenServer.AuthenticationStatus.ClusterAdmin:
                return(null);

            case RavenServer.AuthenticationStatus.Allowed:
                return((database, requireWrite) =>
                {
                    switch (database)
                    {
                    case null:
                        return false;

                    case "*":
                        return true;

                    default:
                        return feature.CanAccess(database, requireAdmin: false, requireWrite: requireWrite);
                    }
                });

            default:
                ThrowInvalidFeatureStatus(status.Value);
                return(null);    // never hit
            }
        }
示例#2
0
        public async Task RemoteWatch()
        {
            var thumbprint = GetStringQueryString("thumbprint", required: false);

            CertificateDefinition clientConnectedCertificate = null;

            var canAccessDatabase = GetDatabaseAccessValidationFunc();

            var currentCertificate = GetCurrentCertificate();

            if (string.IsNullOrEmpty(thumbprint) == false && currentCertificate != null && thumbprint != currentCertificate.Thumbprint)
            {
                using (ServerStore.ContextPool.AllocateOperationContext(out TransactionOperationContext ctx))
                {
                    using (ctx.OpenReadTransaction())
                    {
                        var certByThumbprint = ServerStore.Cluster.GetCertificateByThumbprint(ctx, thumbprint) ?? ServerStore.Cluster.GetLocalStateByThumbprint(ctx, thumbprint);

                        if (certByThumbprint != null)
                        {
                            clientConnectedCertificate = JsonDeserializationServer.CertificateDefinition(certByThumbprint);
                        }
                    }

                    if (clientConnectedCertificate != null)
                    {
                        // we're already connected as ClusterAdmin, here we're just limiting the access to databases based on the thumbprint of the originally connected certificated
                        // so we'll send notifications only about relevant databases

                        var authenticationStatus = new RavenServer.AuthenticateConnection();

                        authenticationStatus.SetBasedOnCertificateDefinition(clientConnectedCertificate);

                        canAccessDatabase = GetDatabaseAccessValidationFunc(authenticationStatus);
                    }
                }
            }

            using (var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync())
            {
                try
                {
                    await SendNotifications(canAccessDatabase, webSocket);
                }
                catch (OperationCanceledException)
                {
                    // ignored
                }
                catch (Exception ex)
                {
                    await HandleException(ex, webSocket);
                }
            }
        }
        public static IEnumerable <RouteInformation> GetAuthorizedRoutes(RavenServer.AuthenticateConnection authenticateConnection, string db = null)
        {
            return(Routes.Where(route =>
            {
                bool authorized = false;
                switch (authenticateConnection.Status)
                {
                case RavenServer.AuthenticationStatus.ClusterAdmin:
                    authorized = true;
                    break;

                case RavenServer.AuthenticationStatus.Operator:
                    if (route.AuthorizationStatus != AuthorizationStatus.ClusterAdmin)
                    {
                        authorized = true;
                    }
                    break;

                case RavenServer.AuthenticationStatus.Allowed:
                    if (route.AuthorizationStatus == AuthorizationStatus.ClusterAdmin || route.AuthorizationStatus == AuthorizationStatus.Operator)
                    {
                        break;
                    }
                    if (route.TypeOfRoute == RouteInformation.RouteType.Databases &&
                        (db == null || authenticateConnection.CanAccess(db, route.AuthorizationStatus == AuthorizationStatus.DatabaseAdmin) == false))
                    {
                        break;
                    }
                    authorized = true;
                    break;

                default:
                    if (route.AuthorizationStatus == AuthorizationStatus.UnauthenticatedClients)
                    {
                        authorized = true;
                    }
                    break;
                }

                return authorized;
            }));
        }
示例#4
0
        public void UnlikelyFailAuthorization(HttpContext context, string database,
                                              RavenServer.AuthenticateConnection feature,
                                              AuthorizationStatus authorizationStatus)
        {
            string message;

            if (feature == null ||
                feature.Status == RavenServer.AuthenticationStatus.None ||
                feature.Status == RavenServer.AuthenticationStatus.NoCertificateProvided)
            {
                message = "This server requires client certificate for authentication, but none was provided by the client.";
            }
            else
            {
                var name = feature.Certificate.FriendlyName;
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.Subject;
                }
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.ToString(false);
                }

                name += "(Thumbprint: " + feature.Certificate.Thumbprint + ")";

                if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarCertificate)
                {
                    message = "The supplied client certificate '" + name + "' is unknown to the server. In order to register your certificate please contact your system administrator.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Allowed)
                {
                    message = "Could not authorize access to " + (database ?? "the server") + " using provided client certificate '" + name + "'.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Operator)
                {
                    message = "Insufficient security clearance to access " + (database ?? "the server") + " using provided client certificate '" + name + "'.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Expired)
                {
                    message = "The supplied client certificate '" + name + "' has expired on " + feature.Certificate.NotAfter.ToString("D") + ". Please contact your system administrator in order to obtain a new one.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.NotYetValid)
                {
                    message = "The supplied client certificate '" + name + "'cannot be used before " + feature.Certificate.NotBefore.ToString("D");
                }
                else
                {
                    message = "Access to the server was denied.";
                }
            }
            switch (authorizationStatus)
            {
            case AuthorizationStatus.ClusterAdmin:
                message += " ClusterAdmin access is required but not given to this certificate";
                break;

            case AuthorizationStatus.Operator:
                message += " Operator/ClusterAdmin access is required but not given to this certificate";
                break;

            case AuthorizationStatus.DatabaseAdmin:
                message += " DatabaseAdmin access is required but not given to this certificate";
                break;
            }

            context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            using (var ctx = JsonOperationContext.ShortTermSingleUse())
                using (var writer = new BlittableJsonTextWriter(ctx, context.Response.Body))
                {
                    DrainRequest(ctx, context);

                    if (RavenServerStartup.IsHtmlAcceptable(context))
                    {
                        context.Response.StatusCode          = (int)HttpStatusCode.Redirect;
                        context.Response.Headers["Location"] = "/auth-error.html?err=" + Uri.EscapeDataString(message);
                        return;
                    }

                    ctx.Write(writer,
                              new DynamicJsonValue
                    {
                        ["Type"]    = "InvalidAuth",
                        ["Message"] = message
                    });
                }
        }
示例#5
0
        public static void UnlikelyFailAuthorization(HttpContext context, string database,
                                                     RavenServer.AuthenticateConnection feature,
                                                     AuthorizationStatus authorizationStatus)
        {
            string message;

            if (feature == null ||
                feature.Status == RavenServer.AuthenticationStatus.None ||
                feature.Status == RavenServer.AuthenticationStatus.NoCertificateProvided)
            {
                message = "This server requires client certificate for authentication, but none was provided by the client.";
            }
            else
            {
                var name = feature.Certificate.FriendlyName;
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.Subject;
                }
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.ToString(false);
                }

                name += $"(Thumbprint: {feature.Certificate.Thumbprint})";

                if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarCertificate)
                {
                    message =
                        $"The supplied client certificate '{name}' is unknown to the server. In order to register your certificate please contact your system administrator.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarIssuer)
                {
                    message =
                        $"The supplied client certificate '{name}' is unknown to the server but has a known Public Key Pinning Hash. Will not use it to authenticate because the issuer is unknown. To fix this, the admin can register the pinning hash of the *issuer* certificate: '{feature.IssuerHash}' in the '{RavenConfiguration.GetKey(x => x.Security.WellKnownIssuerHashes)}' configuration entry.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Allowed)
                {
                    message = $"Could not authorize access to {(database ?? "the server")} using provided client certificate '{name}'.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Operator)
                {
                    message = $"Insufficient security clearance to access {(database ?? "the server")} using provided client certificate '{name}'.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Expired)
                {
                    message =
                        $"The supplied client certificate '{name}' has expired on {feature.Certificate.NotAfter:D}. Please contact your system administrator in order to obtain a new one.";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.NotYetValid)
                {
                    message = $"The supplied client certificate '{name}'cannot be used before {feature.Certificate.NotBefore:D}";
                }
                else
                {
                    message = "Access to the server was denied.";
                }
            }
            switch (authorizationStatus)
            {
            case AuthorizationStatus.ClusterAdmin:
                message += " ClusterAdmin access is required but not given to this certificate";
                break;

            case AuthorizationStatus.Operator:
                message += " Operator/ClusterAdmin access is required but not given to this certificate";
                break;

            case AuthorizationStatus.DatabaseAdmin:
                message += " DatabaseAdmin access is required but not given to this certificate";
                break;
            }

            context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            using (var ctx = JsonOperationContext.ShortTermSingleUse())
                using (var writer = new BlittableJsonTextWriter(ctx, context.Response.Body))
                {
                    DrainRequest(ctx, context);

                    if (RavenServerStartup.IsHtmlAcceptable(context))
                    {
                        context.Response.StatusCode          = (int)HttpStatusCode.Redirect;
                        context.Response.Headers["Location"] = "/auth-error.html?err=" + Uri.EscapeDataString(message);
                        return;
                    }

                    ctx.Write(writer,
                              new DynamicJsonValue
                    {
                        ["Type"]    = "InvalidAuth",
                        ["Message"] = message
                    });
                }
        }
示例#6
0
        internal bool CanAccessRoute(RouteInformation route, HttpContext context, string databaseName, RavenServer.AuthenticateConnection feature, out RavenServer.AuthenticationStatus authenticationStatus)
        {
            authenticationStatus = feature?.Status ?? RavenServer.AuthenticationStatus.None;
            switch (route.AuthorizationStatus)
            {
            case AuthorizationStatus.UnauthenticatedClients:
                var userWantsToAccessStudioMainPage = context.Request.Path == "/studio/index.html";
                if (userWantsToAccessStudioMainPage)
                {
                    switch (authenticationStatus)
                    {
                    case RavenServer.AuthenticationStatus.NoCertificateProvided:
                    case RavenServer.AuthenticationStatus.Expired:
                    case RavenServer.AuthenticationStatus.NotYetValid:
                    case RavenServer.AuthenticationStatus.None:
                    case RavenServer.AuthenticationStatus.UnfamiliarCertificate:
                    case RavenServer.AuthenticationStatus.UnfamiliarIssuer:
                        return(false);
                    }
                }

                return(true);

            case AuthorizationStatus.ClusterAdmin:
            case AuthorizationStatus.Operator:
            case AuthorizationStatus.ValidUser:
            case AuthorizationStatus.DatabaseAdmin:
            case AuthorizationStatus.RestrictedAccess:
                switch (authenticationStatus)
                {
                case RavenServer.AuthenticationStatus.NoCertificateProvided:
                case RavenServer.AuthenticationStatus.Expired:
                case RavenServer.AuthenticationStatus.NotYetValid:
                case RavenServer.AuthenticationStatus.None:
                    return(false);

                case RavenServer.AuthenticationStatus.UnfamiliarCertificate:
                case RavenServer.AuthenticationStatus.UnfamiliarIssuer:
                    // we allow an access to the restricted endpoints with an unfamiliar certificate, since we will authorize it at the endpoint level
                    if (route.AuthorizationStatus == AuthorizationStatus.RestrictedAccess)
                    {
                        return(true);
                    }
                    ;
                    goto case RavenServer.AuthenticationStatus.None;

                case RavenServer.AuthenticationStatus.Allowed:
                    if (route.AuthorizationStatus == AuthorizationStatus.Operator || route.AuthorizationStatus == AuthorizationStatus.ClusterAdmin)
                    {
                        goto case RavenServer.AuthenticationStatus.None;
                    }

                    if (databaseName == null)
                    {
                        return(true);
                    }
                    if (feature.CanAccess(databaseName, route.AuthorizationStatus == AuthorizationStatus.DatabaseAdmin, route.EndpointType == EndpointType.Write))
                    {
                        return(true);
                    }

                    goto case RavenServer.AuthenticationStatus.None;

                case RavenServer.AuthenticationStatus.Operator:
                    if (route.AuthorizationStatus == AuthorizationStatus.ClusterAdmin)
                    {
                        goto case RavenServer.AuthenticationStatus.None;
                    }
                    return(true);

                case RavenServer.AuthenticationStatus.ClusterAdmin:
                    return(true);

                default:
                    throw new ArgumentOutOfRangeException();
                }

            default:
                ThrowUnknownAuthStatus(route);
                return(false);    // never hit
            }
        }
示例#7
0
        public void UnlikelyFailAuthorization(HttpContext context, string database, RavenServer.AuthenticateConnection feature)
        {
            string message;

            if (feature == null || feature.Status == RavenServer.AuthenticationStatus.None || feature.Status == RavenServer.AuthenticationStatus.NoCertificateProvided)
            {
                message = "This server requires client certificate for authentication, but none was provided by the client";
            }
            else
            {
                var name = feature.Certificate.FriendlyName;
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.Subject;
                }
                if (string.IsNullOrWhiteSpace(name))
                {
                    name = feature.Certificate.ToString(false);
                }

                if (feature.Status == RavenServer.AuthenticationStatus.UnfamiliarCertificate)
                {
                    message = "The provided client certificate '" + name + "' is not on the allowed list of certificates that can access this server";
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Allowed)
                {
                    message = "The provided client certificate '" + name + "' is not authorized to access " + (database ?? "the server");
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Operator)
                {
                    message = "The provided client certificate '" + name + "' does not have sufficient level to access " + (database ?? "the server");
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.Expired)
                {
                    message = "The provided client certificate '" + name + "' is expired on " + feature.Certificate.NotAfter;
                }
                else if (feature.Status == RavenServer.AuthenticationStatus.NotYetValid)
                {
                    message = "The provided client certificate '" + name + "' is not yet valid because it starts on " + feature.Certificate.NotBefore;
                }
                else
                {
                    message = "Access to this server was denied, but the reason why is confidential, you did not see this message and your memory will self destruct in 5 seconds.";
                }
            }
            context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
            using (var ctx = JsonOperationContext.ShortTermSingleUse())
                using (var writer = new BlittableJsonTextWriter(ctx, context.Response.Body))
                {
                    DrainRequest(ctx, context);

                    if (RavenServerStartup.IsHtmlAcceptable(context))
                    {
                        context.Response.StatusCode          = (int)HttpStatusCode.Redirect;
                        context.Response.Headers["Location"] = "/studio/auth-error.html?err=" + Uri.EscapeDataString(message);
                        return;
                    }

                    ctx.Write(writer,
                              new DynamicJsonValue
                    {
                        ["Type"]    = "InvalidAuth",
                        ["Message"] = message
                    });
                }
        }