diff --git a/pkg/api/admin_users.go b/pkg/api/admin_users.go deleted file mode 100644 index 638c8b80afa15..0000000000000 --- a/pkg/api/admin_users.go +++ /dev/null @@ -1,555 +0,0 @@ -package api - -import ( - "errors" - "fmt" - "net/http" - "strconv" - - "golang.org/x/sync/errgroup" - - "github.com/grafana/authlib/claims" - "github.com/grafana/grafana/pkg/api/dtos" - "github.com/grafana/grafana/pkg/api/response" - "github.com/grafana/grafana/pkg/apimachinery/identity" - "github.com/grafana/grafana/pkg/infra/metrics" - "github.com/grafana/grafana/pkg/services/accesscontrol" - "github.com/grafana/grafana/pkg/services/auth" - contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" - "github.com/grafana/grafana/pkg/services/login" - "github.com/grafana/grafana/pkg/services/org" - "github.com/grafana/grafana/pkg/services/user" - "github.com/grafana/grafana/pkg/web" -) - -// swagger:route POST /admin/users admin_users adminCreateUser -// -// Create new user. -// -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users:create`. -// Note that OrgId is an optional parameter that can be used to assign a new user to a different organization when `auto_assign_org` is set to `true`. -// -// Security: -// - basic: -// -// Responses: -// 200: adminCreateUserResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 412: preconditionFailedError -// 500: internalServerError -func (hs *HTTPServer) AdminCreateUser(c *contextmodel.ReqContext) response.Response { - form := dtos.AdminCreateUserForm{} - if err := web.Bind(c.Req, &form); err != nil { - return response.Error(http.StatusBadRequest, "bad request data", err) - } - - cmd := user.CreateUserCommand{ - Login: form.Login, - Email: form.Email, - Password: form.Password, - Name: form.Name, - OrgID: form.OrgId, - } - - usr, err := hs.userService.Create(c.Req.Context(), &cmd) - if err != nil { - if errors.Is(err, org.ErrOrgNotFound) { - return response.Error(http.StatusBadRequest, err.Error(), nil) - } - - if errors.Is(err, user.ErrUserAlreadyExists) { - return response.Error(http.StatusPreconditionFailed, fmt.Sprintf("User with email '%s' or username '%s' already exists", form.Email, form.Login), err) - } - - return response.ErrOrFallback(http.StatusInternalServerError, "failed to create user", err) - } - - metrics.MApiAdminUserCreate.Inc() - - result := user.AdminCreateUserResponse{ - Message: "User created", - ID: usr.ID, - } - - return response.JSON(http.StatusOK, result) -} - -// swagger:route PUT /admin/users/{user_id}/password admin_users adminUpdateUserPassword -// -// Set password for user. -// -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.password:update` and scope `global.users:*`. -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 500: internalServerError -func (hs *HTTPServer) AdminUpdateUserPassword(c *contextmodel.ReqContext) response.Response { - if hs.Cfg.DisableLoginForm || hs.Cfg.DisableLogin { - return response.Error(http.StatusForbidden, - "Not allowed to reset password when login form is disabled", nil) - } - - form := dtos.AdminUpdateUserPasswordForm{} - if err := web.Bind(c.Req, &form); err != nil { - return response.Error(http.StatusBadRequest, "bad request data", err) - } - - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - if response := hs.errOnExternalUser(c.Req.Context(), userID); response != nil { - return response - } - - if err := hs.userService.Update(c.Req.Context(), &user.UpdateUserCommand{UserID: userID, Password: &form.Password}); err != nil { - return response.Error(http.StatusInternalServerError, "Failed to update user password", err) - } - - usr, err := hs.userService.GetByID(c.Req.Context(), &user.GetUserByIDQuery{ID: userID}) - if err != nil { - return response.Error(http.StatusInternalServerError, "Could not read user from database", err) - } - - if err := hs.loginAttemptService.Reset(c.Req.Context(), - usr.Login); err != nil { - c.Logger.Warn("could not reset login attempts", "err", err, "username", usr.Login) - } - - if err := hs.AuthTokenService.RevokeAllUserTokens(c.Req.Context(), - userID); err != nil { - return response.Error(http.StatusExpectationFailed, - "User password updated but unable to revoke user sessions", err) - } - - return response.Success("User password updated") -} - -// swagger:route PUT /admin/users/{user_id}/permissions admin_users adminUpdateUserPermissions -// -// Set permissions for user. -// -// Only works with Basic Authentication (username and password). See introduction for an explanation. -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.permissions:update` and scope `global.users:*`. -// -// Responses: -// 200: okResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 500: internalServerError -func (hs *HTTPServer) AdminUpdateUserPermissions(c *contextmodel.ReqContext) response.Response { - form := dtos.AdminUpdateUserPermissionsForm{} - if err := web.Bind(c.Req, &form); err != nil { - return response.Error(http.StatusBadRequest, "bad request data", err) - } - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - if authInfo, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &login.GetAuthInfoQuery{UserId: userID}); err == nil && authInfo != nil { - oauthInfo := hs.SocialService.GetOAuthInfoProvider(authInfo.AuthModule) - if login.IsGrafanaAdminExternallySynced(hs.Cfg, oauthInfo, authInfo.AuthModule) { - return response.Error(http.StatusForbidden, "Cannot change Grafana Admin role for externally synced user", nil) - } - } - - err = hs.userService.Update(c.Req.Context(), &user.UpdateUserCommand{ - UserID: userID, - IsGrafanaAdmin: &form.IsGrafanaAdmin, - }) - if err != nil { - if errors.Is(err, user.ErrLastGrafanaAdmin) { - return response.Error(http.StatusBadRequest, user.ErrLastGrafanaAdmin.Error(), nil) - } - - return response.Error(http.StatusInternalServerError, "Failed to update user permissions", err) - } - - return response.Success("User permissions updated") -} - -// swagger:route DELETE /admin/users/{user_id} admin_users adminDeleteUser -// -// Delete global User. -// -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users:delete` and scope `global.users:*`. -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 401: unauthorisedError -// 403: forbiddenError -// 404: notFoundError -// 500: internalServerError -func (hs *HTTPServer) AdminDeleteUser(c *contextmodel.ReqContext) response.Response { - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - cmd := user.DeleteUserCommand{UserID: userID} - - if err := hs.userService.Delete(c.Req.Context(), &cmd); err != nil { - if errors.Is(err, user.ErrUserNotFound) { - return response.Error(http.StatusNotFound, user.ErrUserNotFound.Error(), nil) - } - return response.Error(http.StatusInternalServerError, "Failed to delete user", err) - } - - g, ctx := errgroup.WithContext(c.Req.Context()) - g.Go(func() error { - if err := hs.starService.DeleteByUser(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.orgService.DeleteUserFromAll(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.preferenceService.DeleteByUser(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.TeamService.RemoveUsersMemberships(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.authInfoService.DeleteUserAuthInfo(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.AuthTokenService.RevokeAllUserTokens(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.QuotaService.DeleteQuotaForUser(ctx, cmd.UserID); err != nil { - return err - } - return nil - }) - g.Go(func() error { - if err := hs.accesscontrolService.DeleteUserPermissions(ctx, accesscontrol.GlobalOrgID, cmd.UserID); err != nil { - return err - } - return nil - }) - if err := g.Wait(); err != nil { - return response.Error(http.StatusInternalServerError, "Failed to delete user", err) - } - - return response.Success("User deleted") -} - -// swagger:route POST /admin/users/{user_id}/disable admin_users adminDisableUser -// -// Disable user. -// -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users:disable` and scope `global.users:1` (userIDScope). -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 401: unauthorisedError -// 403: forbiddenError -// 404: notFoundError -// 500: internalServerError -func (hs *HTTPServer) AdminDisableUser(c *contextmodel.ReqContext) response.Response { - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - // External users shouldn't be disabled from API - authInfoQuery := &login.GetAuthInfoQuery{UserId: userID} - if _, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), authInfoQuery); !errors.Is(err, user.ErrUserNotFound) { - return response.Error(http.StatusInternalServerError, "Could not disable external user", nil) - } - - isDisabled := true - if err := hs.userService.Update(c.Req.Context(), &user.UpdateUserCommand{UserID: userID, IsDisabled: &isDisabled}); err != nil { - if errors.Is(err, user.ErrUserNotFound) { - return response.Error(http.StatusNotFound, user.ErrUserNotFound.Error(), nil) - } - return response.Error(http.StatusInternalServerError, "Failed to disable user", err) - } - - err = hs.AuthTokenService.RevokeAllUserTokens(c.Req.Context(), userID) - if err != nil { - return response.Error(http.StatusInternalServerError, "Failed to disable user", err) - } - - return response.Success("User disabled") -} - -// swagger:route POST /admin/users/{user_id}/enable admin_users adminEnableUser -// -// Enable user. -// -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users:enable` and scope `global.users:1` (userIDScope). -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 401: unauthorisedError -// 403: forbiddenError -// 404: notFoundError -// 500: internalServerError -func (hs *HTTPServer) AdminEnableUser(c *contextmodel.ReqContext) response.Response { - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - // External users shouldn't be disabled from API - authInfoQuery := &login.GetAuthInfoQuery{UserId: userID} - if _, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), authInfoQuery); !errors.Is(err, user.ErrUserNotFound) { - return response.Error(http.StatusInternalServerError, "Could not enable external user", nil) - } - - isDisabled := false - if err := hs.userService.Update(c.Req.Context(), &user.UpdateUserCommand{UserID: userID, IsDisabled: &isDisabled}); err != nil { - if errors.Is(err, user.ErrUserNotFound) { - return response.Error(http.StatusNotFound, user.ErrUserNotFound.Error(), nil) - } - return response.Error(http.StatusInternalServerError, "Failed to enable user", err) - } - - return response.Success("User enabled") -} - -// swagger:route POST /admin/users/{user_id}/logout admin_users adminLogoutUser -// -// Logout user revokes all auth tokens (devices) for the user. User of issued auth tokens (devices) will no longer be logged in and will be required to authenticate again upon next activity. -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.logout` and scope `global.users:*`. -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 404: notFoundError -// 500: internalServerError -func (hs *HTTPServer) AdminLogoutUser(c *contextmodel.ReqContext) response.Response { - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - if c.SignedInUser.GetID() == identity.NewTypedID(claims.TypeUser, userID) { - return response.Error(http.StatusBadRequest, "You cannot logout yourself", nil) - } - - return hs.logoutUserFromAllDevicesInternal(c.Req.Context(), userID) -} - -// swagger:route GET /admin/users/{user_id}/auth-tokens admin_users adminGetUserAuthTokens -// -// Return a list of all auth tokens (devices) that the user currently have logged in from. -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.authtoken:list` and scope `global.users:*`. -// -// Security: -// - basic: -// -// Responses: -// 200: adminGetUserAuthTokensResponse -// 401: unauthorisedError -// 403: forbiddenError -// 500: internalServerError -func (hs *HTTPServer) AdminGetUserAuthTokens(c *contextmodel.ReqContext) response.Response { - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - return hs.getUserAuthTokensInternal(c, userID) -} - -// swagger:route POST /admin/users/{user_id}/revoke-auth-token admin_users adminRevokeUserAuthToken -// -// Revoke auth token for user. -// -// Revokes the given auth token (device) for the user. User of issued auth token (device) will no longer be logged in and will be required to authenticate again upon next activity. -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.authtoken:update` and scope `global.users:*`. -// -// Security: -// - basic: -// -// Responses: -// 200: okResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 404: notFoundError -// 500: internalServerError -func (hs *HTTPServer) AdminRevokeUserAuthToken(c *contextmodel.ReqContext) response.Response { - cmd := auth.RevokeAuthTokenCmd{} - if err := web.Bind(c.Req, &cmd); err != nil { - return response.Error(http.StatusBadRequest, "bad request data", err) - } - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - return hs.revokeUserAuthTokenInternal(c, userID, cmd) -} - -// swagger:route PUT /admin/users/{user_id}/permissions admin_users AdminAddUserOAuth -// -// # User OAuth mapping added -// -// Only works with Basic Authentication (username and password). See introduction for an explanation. -// If you are running Grafana Enterprise and have Fine-grained access control enabled, you need to have a permission with action `users.permissions:update` and scope `global.users:*`. -// -// Responses: -// 200: okResponse -// 400: badRequestError -// 401: unauthorisedError -// 403: forbiddenError -// 500: internalServerError -func (hs *HTTPServer) AdminAddUserOAuth(c *contextmodel.ReqContext) response.Response { - form := dtos.AdminAddUserOAuthForm{} - if err := web.Bind(c.Req, &form); err != nil { - return response.Error(http.StatusBadRequest, "bad request data", err) - } - userID, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64) - if err != nil { - return response.Error(http.StatusBadRequest, "id is invalid", err) - } - - if authInfo, err := hs.authInfoService.GetAuthInfo(c.Req.Context(), &login.GetAuthInfoQuery{UserId: userID}); err == nil && authInfo != nil { - oauthInfo := hs.SocialService.GetOAuthInfoProvider(authInfo.AuthModule) - if login.IsGrafanaAdminExternallySynced(hs.Cfg, oauthInfo, authInfo.AuthModule) { - return response.Error(http.StatusForbidden, "Cannot change Grafana Admin role for externally synced user", nil) - } - } - - err = hs.userService.UpdateAuthModule(c.Req.Context(), &user.UpdateAuthModuleCommand{ - UserID: userID, - AuthModule: form.AuthModule, - AuthID: form.AuthID, - }) - if err != nil { - if errors.Is(err, user.ErrLastGrafanaAdmin) { - return response.Error(http.StatusBadRequest, user.ErrLastGrafanaAdmin.Error(), nil) - } - - return response.Error(http.StatusInternalServerError, "Failed to create user OAuth mapping", err) - } - - return response.Success("User OAuth mapping added") -} - -// swagger:parameters adminUpdateUserPassword -type AdminUpdateUserPasswordParams struct { - // in:body - // required:true - Body dtos.AdminUpdateUserPasswordForm `json:"body"` - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminDeleteUser -type AdminDeleteUserParams struct { - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminEnableUser -type AdminEnableUserParams struct { - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminDisableUser -type AdminDisableUserParams struct { - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminGetUserAuthTokens -type AdminGetUserAuthTokensParams struct { - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminLogoutUser -type AdminLogoutUserParams struct { - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminRevokeUserAuthToken -type AdminRevokeUserAuthTokenParams struct { - // in:body - // required:true - Body auth.RevokeAuthTokenCmd `json:"body"` - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:parameters adminCreateUser -type AdminCreateUserParams struct { - // in:body - // required:true - Body dtos.AdminCreateUserForm `json:"body"` -} - -// swagger:parameters adminUpdateUserPermissions -type AdminUpdateUserPermissionsParams struct { - // in:body - // required:true - Body dtos.AdminUpdateUserPermissionsForm `json:"body"` - // in:path - // required:true - UserID int64 `json:"user_id"` -} - -// swagger:response adminCreateUserResponse -type AdminCreateUserResponseResponse struct { - // in:body - Body user.AdminCreateUserResponse `json:"body"` -} - -// swagger:response adminGetUserAuthTokensResponse -type AdminGetUserAuthTokensResponse struct { - // in:body - Body []*auth.UserToken `json:"body"` -} diff --git a/pkg/api/api.go b/pkg/api/api.go index 326864416cf5f..e79526134cc5e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -588,9 +588,6 @@ func (hs *HTTPServer) registerRoutes() { adminUserRoute.Post("/:id/logout", authorizeInOrg(ac.UseGlobalOrg, ac.EvalPermission(ac.ActionUsersLogout, userIDScope)), routing.Wrap(hs.AdminLogoutUser)) adminUserRoute.Get("/:id/auth-tokens", authorizeInOrg(ac.UseGlobalOrg, ac.EvalPermission(ac.ActionUsersAuthTokenList, userIDScope)), routing.Wrap(hs.AdminGetUserAuthTokens)) adminUserRoute.Post("/:id/revoke-auth-token", authorizeInOrg(ac.UseGlobalOrg, ac.EvalPermission(ac.ActionUsersAuthTokenUpdate, userIDScope)), routing.Wrap(hs.AdminRevokeUserAuthToken)) - - adminUserRoute.Post("/:id/oauth", reqGrafanaAdmin, authorizeInOrg(ac.UseGlobalOrg, ac.EvalPermission(ac.ActionUsersPermissionsUpdate, userIDScope)), routing.Wrap(hs.AdminAddUserOAuth)) - }, reqSignedIn) // rendering diff --git a/pkg/api/dtos/user.go b/pkg/api/dtos/user.go index e7409954cca67..35cdb3c78b039 100644 --- a/pkg/api/dtos/user.go +++ b/pkg/api/dtos/user.go @@ -46,9 +46,3 @@ type UserLookupDTO struct { Login string `json:"login"` AvatarURL string `json:"avatarUrl"` } - -type AdminAddUserOAuthForm struct { - UserID int64 `json:"user_id"` - AuthModule string `json:"auth_module"` - AuthID string `json:"auth_id"` -} diff --git a/pkg/api/login_oauth.go b/pkg/api/login_oauth.go index 2bc3c0509a8d3..51a0857f96ad1 100644 --- a/pkg/api/login_oauth.go +++ b/pkg/api/login_oauth.go @@ -1,8 +1,7 @@ package api import ( - // "github.com/grafana/grafana/pkg/apimachinery/errutil" - + "github.com/grafana/grafana/pkg/apimachinery/errutil" "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/middleware/cookies" "github.com/grafana/grafana/pkg/services/authn" @@ -22,9 +21,7 @@ func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) { if errorParam := reqCtx.Query("error"); errorParam != "" { errorDesc := reqCtx.Query("error_description") hs.log.Error("failed to login ", "error", errorParam, "errorDesc", errorDesc) - // hs.redirectWithError(reqCtx, errutil.Unauthorized("oauth.login", errutil.WithPublicMessage(hs.Cfg.OAuthLoginErrorMessage)).Errorf("Login provider denied login request")) - - reqCtx.Redirect("/login?disableAutoLogin=true") + hs.redirectWithError(reqCtx, errutil.Unauthorized("oauth.login", errutil.WithPublicMessage(hs.Cfg.OAuthLoginErrorMessage)).Errorf("Login provider denied login request")) return } @@ -48,14 +45,6 @@ func (hs *HTTPServer) OAuthLogin(reqCtx *contextmodel.ReqContext) { cookies.WriteCookie(reqCtx.Resp, OauthPKCECookieName, pkce, hs.Cfg.OAuthCookieMaxAge, hs.CookieOptionsFromCfg) } - // autoLogin := reqCtx.Query("autologin") - // if autoLogin == "true" { - // if strings.Contains(redirect.URL, "?") { - // redirect.URL += "&prompt=none" - // } else { - // redirect.URL += "?prompt=none" - // } - // } reqCtx.Redirect(redirect.URL) return } diff --git a/pkg/events/events.go b/pkg/events/events.go index 73b12a691407a..38b6dce4090ac 100644 --- a/pkg/events/events.go +++ b/pkg/events/events.go @@ -47,13 +47,6 @@ type UserUpdated struct { Email string `json:"email"` } -type UserAuthUpdated struct { - Timestamp time.Time `json:"timestamp"` - UserID int64 `json:"user_id"` - AuthModule string `json:"auth_module"` - AuthID string `json:"auth_id"` -} - type DataSourceDeleted struct { Timestamp time.Time `json:"timestamp"` Name string `json:"name"` diff --git a/pkg/login/social/social.go b/pkg/login/social/social.go index a8a28a867c8e7..12cf2dfd0a752 100644 --- a/pkg/login/social/social.go +++ b/pkg/login/social/social.go @@ -77,7 +77,6 @@ type OAuthInfo struct { OrgMapping []string `mapstructure:"org_mapping"` Scopes []string `mapstructure:"scopes" toml:"scopes"` SignoutRedirectUrl string `mapstructure:"signout_redirect_url" toml:"signout_redirect_url"` - SignoutUrl string `mapstructure:"signout_url" toml:"signout_url"` SkipOrgRoleSync bool `mapstructure:"skip_org_role_sync" toml:"skip_org_role_sync"` TeamIdsAttributePath string `mapstructure:"team_ids_attribute_path" toml:"team_ids_attribute_path"` TeamsUrl string `mapstructure:"teams_url" toml:"teams_url"` diff --git a/pkg/models/usertoken/user_token.go b/pkg/models/usertoken/user_token.go index ef66905e4d983..ea661d1d3221a 100644 --- a/pkg/models/usertoken/user_token.go +++ b/pkg/models/usertoken/user_token.go @@ -36,7 +36,6 @@ type UserToken struct { UpdatedAt int64 RevokedAt int64 UnhashedToken string - IdToken string } const UrgentRotateTime = 1 * time.Minute diff --git a/pkg/services/auth/auth.go b/pkg/services/auth/auth.go index fefb56133dd41..88ff4d0615735 100644 --- a/pkg/services/auth/auth.go +++ b/pkg/services/auth/auth.go @@ -71,7 +71,6 @@ type CreateTokenCommand struct { ClientIP net.IP UserAgent string ExternalSession *ExternalSession - IdToken string } // UserTokenService are used for generating and validating user tokens diff --git a/pkg/services/auth/authimpl/auth_token.go b/pkg/services/auth/authimpl/auth_token.go index 4927a05d7f6ca..a42784d267011 100644 --- a/pkg/services/auth/authimpl/auth_token.go +++ b/pkg/services/auth/authimpl/auth_token.go @@ -94,7 +94,6 @@ func (s *UserAuthTokenService) CreateToken(ctx context.Context, cmd *auth.Create SeenAt: 0, RevokedAt: 0, AuthTokenSeen: false, - IdToken: cmd.IdToken, } err = s.sqlStore.InTransaction(ctx, func(ctx context.Context) error { @@ -124,11 +123,6 @@ func (s *UserAuthTokenService) CreateToken(ctx context.Context, cmd *auth.Create var userToken auth.UserToken err = userAuthToken.toUserToken(&userToken) - // 메모리저장 테스트 - // if cmd.IdToken != "" { - // userToken.IdToken = cmd.IdToken - // } - return &userToken, err } diff --git a/pkg/services/auth/authimpl/model.go b/pkg/services/auth/authimpl/model.go index a961712813b05..fa0e566cf8f2c 100644 --- a/pkg/services/auth/authimpl/model.go +++ b/pkg/services/auth/authimpl/model.go @@ -21,7 +21,6 @@ type userAuthToken struct { RevokedAt int64 UnhashedToken string `xorm:"-"` ExternalSessionId int64 - IdToken string } func userAuthTokenFromUserToken(ut *auth.UserToken) (*userAuthToken, error) { @@ -72,6 +71,5 @@ func (uat *userAuthToken) toUserToken(ut *auth.UserToken) error { ut.RevokedAt = uat.RevokedAt ut.UnhashedToken = uat.UnhashedToken ut.ExternalSessionId = uat.ExternalSessionId - ut.IdToken = uat.IdToken return nil } diff --git a/pkg/services/authn/authnimpl/service.go b/pkg/services/authn/authnimpl/service.go index 1603cdd69d70a..4b24f92063b67 100644 --- a/pkg/services/authn/authnimpl/service.go +++ b/pkg/services/authn/authnimpl/service.go @@ -3,9 +3,7 @@ package authnimpl import ( "context" "errors" - "fmt" "net/http" - "net/url" "strconv" "strings" @@ -243,19 +241,13 @@ func (s *Service) Login(ctx context.Context, client string, r *authn.Request) (i s.log.FromContext(ctx).Debug("Failed to parse ip from address", "client", c.Name(), "id", id.ID, "addr", addr, "error", err) } - var sessionToken *auth.UserToken - var sessionTokenErr error externalSession := s.resolveExternalSessionFromIdentity(ctx, id, userID) - if externalSession != nil && externalSession.IDToken != "" { - sessionToken, sessionTokenErr = s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession, IdToken: externalSession.IDToken}) - } else { - sessionToken, sessionTokenErr = s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession}) - } - if sessionTokenErr != nil { + sessionToken, err := s.sessionService.CreateToken(ctx, &auth.CreateTokenCommand{User: &user.User{ID: userID}, ClientIP: ip, UserAgent: r.HTTPRequest.UserAgent(), ExternalSession: externalSession}) + if err != nil { s.metrics.failedLogin.WithLabelValues(client).Inc() - s.log.FromContext(ctx).Error("Failed to create session", "client", client, "id", id.ID, "err", sessionTokenErr) - return nil, sessionTokenErr + s.log.FromContext(ctx).Error("Failed to create session", "client", client, "id", id.ID, "err", err) + return nil, err } s.metrics.successfulLogin.WithLabelValues(client).Inc() @@ -335,19 +327,6 @@ func (s *Service) Logout(ctx context.Context, user identity.Requester, sessionTo goto Default } - if s.cfg.SignoutRedirectUrl != "" { - rawRedirect := s.cfg.AppURL + "login?disableAutoLogin=true" - idToken := sessionToken.IdToken - - logoutURL := fmt.Sprintf( - "%s?post_logout_redirect_uri=%s&id_token_hint=%s", - s.cfg.SignoutUrl, - url.QueryEscape(rawRedirect), - idToken, - ) - clientRedirect.URL = logoutURL - } - redirect = clientRedirect } diff --git a/pkg/services/ssosettings/strategies/oauth_strategy.go b/pkg/services/ssosettings/strategies/oauth_strategy.go index b58c9f6bb12d0..2a9d70a1012d2 100644 --- a/pkg/services/ssosettings/strategies/oauth_strategy.go +++ b/pkg/services/ssosettings/strategies/oauth_strategy.go @@ -102,7 +102,6 @@ func (s *OAuthStrategy) loadSettingsForProvider(provider string) map[string]any "auto_login": section.Key("auto_login").MustBool(false), "allowed_groups": section.Key("allowed_groups").Value(), "signout_redirect_url": section.Key("signout_redirect_url").Value(), - "signout_url": section.Key("signout_url").Value(), "org_mapping": section.Key("org_mapping").Value(), "org_attribute_path": section.Key("org_attribute_path").Value(), } diff --git a/pkg/services/user/model.go b/pkg/services/user/model.go index 57a3d9638d144..fe9d6e75cc7d4 100644 --- a/pkg/services/user/model.go +++ b/pkg/services/user/model.go @@ -49,14 +49,6 @@ type User struct { LastSeenAt time.Time } -type UserAuth struct { - ID int64 `xorm:"pk autoincr 'id'" json:"id"` - UserID int64 `xorm:"user_id" json:"user_id"` - AuthModule string `xorm:"auth_module" json:"auth_module"` - AuthID string `xorm:"auth_id" json:"auth_id"` - Created time.Time `xorm:"created" json:"created"` -} - type CreateUserCommand struct { UID string Email string @@ -101,13 +93,6 @@ type UpdateUserCommand struct { HelpFlags1 *HelpFlags1 `json:"-"` } -type UpdateAuthModuleCommand struct { - UserID int64 `json:"user_id"` - AuthModule string `json:"auth_module"` - AuthID string `json:"auth_id"` - Created time.Time `xorm:"created" json:"created"` -} - type UpdateUserLastSeenAtCommand struct { UserID int64 OrgID int64 diff --git a/pkg/services/user/userimpl/store.go b/pkg/services/user/userimpl/store.go index 8eb383367a697..5cfc775d16d31 100644 --- a/pkg/services/user/userimpl/store.go +++ b/pkg/services/user/userimpl/store.go @@ -26,7 +26,6 @@ type store interface { Delete(context.Context, int64) error LoginConflict(ctx context.Context, login, email string) error Update(context.Context, *user.UpdateUserCommand) error - UpdateAuthModule(context.Context, *user.UpdateAuthModuleCommand) error UpdateLastSeenAt(context.Context, *user.UpdateUserLastSeenAtCommand) error GetSignedInUser(context.Context, *user.GetSignedInUserQuery) (*user.SignedInUser, error) GetProfile(context.Context, *user.GetUserProfileQuery) (*user.UserProfileDTO, error) @@ -282,43 +281,6 @@ func (ss *sqlStore) Update(ctx context.Context, cmd *user.UpdateUserCommand) err }) } -func (ss *sqlStore) UpdateAuthModule(ctx context.Context, cmd *user.UpdateAuthModuleCommand) error { - // enforcement of lowercase due to forcement of caseinsensitive login - return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error { - usr := user.UserAuth{ - UserID: cmd.UserID, - AuthModule: cmd.AuthModule, - AuthID: cmd.AuthID, - Created: time.Now(), - } - - // 기존 레코드 조회 (서비스 계정 필터 제거) - q := sess.ID(cmd.UserID) - - rows, err := q.Update(&usr) - if err != nil { - return err - } - - // 존재하지 않으면 Insert - if rows == 0 { - if _, err := sess.Insert(&usr); err != nil { - return err - } - } - - // 이벤트 publish - sess.PublishAfterCommit(&events.UserAuthUpdated{ - Timestamp: usr.Created, - UserID: usr.UserID, - AuthModule: usr.AuthModule, - AuthID: usr.AuthID, - }) - - return nil - }) -} - func (ss *sqlStore) UpdateLastSeenAt(ctx context.Context, cmd *user.UpdateUserLastSeenAtCommand) error { if cmd.UserID <= 0 { return user.ErrUpdateInvalidID diff --git a/pkg/services/user/userimpl/user.go b/pkg/services/user/userimpl/user.go index 050ad73c44b7c..e50855b48b5de 100644 --- a/pkg/services/user/userimpl/user.go +++ b/pkg/services/user/userimpl/user.go @@ -291,22 +291,6 @@ func (s *Service) Update(ctx context.Context, cmd *user.UpdateUserCommand) error return s.store.Update(ctx, cmd) } -func (s *Service) UpdateAuthModule(ctx context.Context, cmd *user.UpdateAuthModuleCommand) error { - ctx, span := s.tracer.Start(ctx, "user.UpdateAuthModule", trace.WithAttributes( - attribute.Int64("userID", cmd.UserID), - )) - defer span.End() - - _, err := s.store.GetByID(ctx, cmd.UserID) - if err != nil { - return err - } - - cmd.Created = time.Now().UTC() - - return s.store.UpdateAuthModule(ctx, cmd) -} - func (s *Service) UpdateLastSeenAt(ctx context.Context, cmd *user.UpdateUserLastSeenAtCommand) error { ctx, span := s.tracer.Start(ctx, "user.UpdateLastSeen", trace.WithAttributes( attribute.Int64("userID", cmd.UserID), diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index c3863cdbde246..2d3620a2ce463 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -175,12 +175,12 @@ type Cfg struct { // CSPReportEnabled toggles Content Security Policy Report Only support. CSPReportOnlyEnabled bool // CSPReportOnlyTemplate contains the Content Security Policy Report Only template. - CSPReportOnlyTemplate string - AngularSupportEnabled bool - EnableFrontendSandboxForPlugins []string - DisableGravatar bool - DataProxyWhiteList map[string]bool - ActionsAllowPostURL string + CSPReportOnlyTemplate string + AngularSupportEnabled bool + DisableFrontendSandboxForPlugins []string + DisableGravatar bool + DataProxyWhiteList map[string]bool + ActionsAllowPostURL string TempDataLifetime time.Duration @@ -242,7 +242,6 @@ type Cfg struct { AdminEmail string DisableLoginForm bool SignoutRedirectUrl string - SignoutUrl string IDResponseHeaderEnabled bool IDResponseHeaderPrefix string IDResponseHeaderNamespaces map[string]struct{} @@ -533,7 +532,6 @@ type Cfg struct { // Unified Storage UnifiedStorage map[string]UnifiedStorageConfig - IndexPath string } type UnifiedStorageConfig struct { @@ -1340,9 +1338,8 @@ func (cfg *Cfg) parseINIFile(iniFile *ini.File) error { cfg.ScopesListScopesURL = scopesSection.Key("list_scopes_endpoint").MustString("") cfg.ScopesListDashboardsURL = scopesSection.Key("list_dashboards_endpoint").MustString("") - // unified storage config + // read unifed storage config cfg.setUnifiedStorageConfig() - cfg.setIndexPath() return nil } @@ -1558,10 +1555,10 @@ func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error { cfg.CSPReportOnlyEnabled = security.Key("content_security_policy_report_only").MustBool(false) cfg.CSPReportOnlyTemplate = security.Key("content_security_policy_report_only_template").MustString("") - enableFrontendSandboxForPlugins := security.Key("enable_frontend_sandbox_for_plugins").MustString("") - for _, plug := range strings.Split(enableFrontendSandboxForPlugins, ",") { + disableFrontendSandboxForPlugins := security.Key("disable_frontend_sandbox_for_plugins").MustString("") + for _, plug := range strings.Split(disableFrontendSandboxForPlugins, ",") { plug = strings.TrimSpace(plug) - cfg.EnableFrontendSandboxForPlugins = append(cfg.EnableFrontendSandboxForPlugins, plug) + cfg.DisableFrontendSandboxForPlugins = append(cfg.DisableFrontendSandboxForPlugins, plug) } if cfg.CSPEnabled && cfg.CSPTemplate == "" { @@ -1630,7 +1627,6 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) { cfg.OAuthCookieMaxAge = auth.Key("oauth_state_cookie_max_age").MustInt(600) cfg.OAuthRefreshTokenServerLockMinWaitMs = auth.Key("oauth_refresh_token_server_lock_min_wait_ms").MustInt64(1000) cfg.SignoutRedirectUrl = valueAsString(auth, "signout_redirect_url", "") - cfg.SignoutUrl = valueAsString(auth, "signout_url", "") // Deprecated cfg.OAuthSkipOrgRoleUpdateSync = false