Beispiel #1
0
        public async Task <IActionResult> Logout([FromForm] LogoutFormData request)
        {
            var existingSession = await HttpContext.Request.Cookies.GetSession(database);

            if (existingSession?.User == null)
            {
                return(BadRequest("You are not currently logged in"));
            }

            if (!csrfVerifier.IsValidCSRFToken(request.CSRF, existingSession.User))
            {
                return(BadRequest("Invalid CSRF token, please try refreshing and then try again"));
            }

            // Session version doesn't need to be enforced here as logging out a session should always be safe
            // (after the above checks)

            // TODO: if an in-progress signature exists, should the session be just converted to a logged out one?

            database.Sessions.Remove(existingSession);
            await database.SaveChangesAsync();

            logger.LogInformation("Session {Id} logged out", existingSession.Id);

            Response.Cookies.Delete(AppInfo.SessionCookieName);
            return(Redirect("/login"));
        }
Beispiel #2
0
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            if (context.Request.Headers.TryGetValue("X-CSRF-Token", out StringValues headerValues))
            {
                if (headerValues.Count < 1 || string.IsNullOrEmpty(headerValues[0]))
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("CSRF token header is empty");

                    return;
                }

                User?user = null;

                if (context.Items.TryGetValue(AppInfo.CurrentUserMiddlewareKey, out object?userRaw))
                {
                    user = userRaw as User;
                }

                if (!csrfVerifier.IsValidCSRFToken(headerValues[0], user))
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("CSRF token is invalid. Please refresh and try again.");

                    return;
                }

                context.Items[AppInfo.CSRFStatusName] = true;
            }
            else if (context.Items.ContainsKey(AppInfo.CSRFNeededName))
            {
                // Download endpoints (for usability with direct links) don't require this
                if (!context.Request.Path.StartsWithSegments("/api/v1/download") &&
                    !context.Request.Path.StartsWithSegments("/api/v1/download_lfs") &&
                    !context.Request.Path.StartsWithSegments("/api/v1/feed"))
                {
                    context.Response.StatusCode = StatusCodes.Status400BadRequest;
                    await context.Response.WriteAsync("CSRF token is required for this request.");

                    return;
                }
            }

            await next.Invoke(context);
        }
        public async Task <IActionResult> Post(RegistrationFormData request)
        {
            if (!csrfVerifier.IsValidCSRFToken(request.CSRF, null, false))
            {
                return(BadRequest("Invalid CSRF"));
            }

            if (!SecurityHelpers.SlowEquals(request.RegistrationCode, configuration.RegistrationCode))
            {
                return(BadRequest("Invalid registration code"));
            }

            if (!request.Email.Contains('@'))
            {
                return(BadRequest("Email is invalid"));
            }

            // Check for conflicting username or email
            if (await database.Users.FirstOrDefaultAsync(u => u.UserName == request.Name) != null ||
                await database.Users.FirstOrDefaultAsync(u => u.Email == request.Email) != null)
            {
                return(BadRequest("There is already an account associated with the given email or name"));
            }

            var password = Passwords.CreateSaltedPasswordHash(request.Password);

            var user = new User()
            {
                Email        = request.Email,
                UserName     = request.Name,
                PasswordHash = password,
                Local        = true
            };

            await database.Users.AddAsync(user);

            Models.User.OnNewUserCreated(user, jobClient);
            await database.SaveChangesAsync();

            logger.LogInformation("New user registered {Name} ({Email})", request.Name, request.Email);

            return(Created($"/users/{user.Id}", user.GetInfo(RecordAccessLevel.Private)));
        }
        public override async Task OnConnectedAsync()
        {
            var     http          = Context.GetHttpContext();
            User?   connectedUser = null;
            Session?session       = null;

            if (http != null)
            {
                var queryParams = http.Request.Query;

                if (!queryParams.TryGetValue("minorVersion", out StringValues minorStr) ||
                    !queryParams.TryGetValue("majorVersion", out StringValues majorStr))
                {
                    throw new HubException("invalid connection parameters");
                }

                if (minorStr.Count < 1 || majorStr.Count < 1)
                {
                    throw new HubException("invalid connection parameters");
                }

                string csrf;

                if (!queryParams.TryGetValue("access_token", out StringValues accessToken))
                {
                    // In release mode (at least I saw this happen once) the access token is in a header
                    if (http.Request.Headers.TryGetValue("Authorization", out StringValues header) &&
                        header.Count > 0 && header[0].StartsWith("Bearer "))
                    {
                        // In format "Bearer TOKEN"
                        csrf = header[0].Split(' ').Last();
                    }
                    else
                    {
                        throw new HubException("invalid connection parameters");
                    }
                }
                else
                {
                    if (accessToken.Count < 1)
                    {
                        throw new HubException("invalid connection parameters");
                    }

                    csrf = accessToken[0];
                }

                try
                {
                    session = await http.Request.Cookies.GetSession(database);

                    connectedUser = await
                                    UserFromCookiesHelper.GetUserFromSession(session, database, true,
                                                                             http.Connection.RemoteIpAddress);
                }
                catch (ArgumentException)
                {
                    throw new HubException("invalid session cookie");
                }

                if (!csrfVerifier.IsValidCSRFToken(csrf, connectedUser))
                {
                    throw new HubException("invalid CSRF token");
                }

                Context.Items["Session"] = session;

                bool invalidVersion = false;

                try
                {
                    var major = Convert.ToInt32(majorStr[0]);
                    var minor = Convert.ToInt32(minorStr[0]);

                    if (major != AppInfo.Major || minor != AppInfo.Minor)
                    {
                        invalidVersion = true;
                    }
                }
                catch (Exception)
                {
                    throw new HubException("invalid connection parameters");
                }

                if (invalidVersion)
                {
                    await Clients.Caller.ReceiveVersionMismatch();
                }
            }

            if (connectedUser != null && session == null)
            {
                throw new Exception("Logic error! user is not null but session is null");
            }

            Context.Items["User"] = connectedUser;

            await base.OnConnectedAsync();

            if (connectedUser == null)
            {
                await Clients.Caller.ReceiveOwnUserInfo(null);
            }
            else
            {
                if (session == null)
                {
                    throw new Exception("logic error, session is null when connected user is not null");
                }

                // All sessions listen to notifications about them
                await Groups.AddToGroupAsync(Context.ConnectionId,
                                             NotificationGroups.SessionImportantMessage + session.Id);

                await Clients.Caller.ReceiveOwnUserInfo(connectedUser.GetInfo(
                                                            connectedUser.HasAccessLevel(UserAccessLevel.Admin) ?
                                                            RecordAccessLevel.Admin :
                                                            RecordAccessLevel.Private));

                // Could send some user specific notices here
                // await Clients.Caller.ReceiveSiteNotice(SiteNoticeType.Primary, "hey you connected");
            }
        }