1package middleware 2 3import ( 4 "errors" 5 "net/url" 6 "regexp" 7 "strconv" 8 "strings" 9 10 "github.com/grafana/grafana/pkg/middleware/cookies" 11 "github.com/grafana/grafana/pkg/models" 12 "github.com/grafana/grafana/pkg/services/sqlstore" 13 "github.com/grafana/grafana/pkg/setting" 14 "github.com/grafana/grafana/pkg/web" 15) 16 17type AuthOptions struct { 18 ReqGrafanaAdmin bool 19 ReqSignedIn bool 20 ReqNoAnonynmous bool 21} 22 23func accessForbidden(c *models.ReqContext) { 24 if c.IsApiRequest() { 25 c.JsonApiErr(403, "Permission denied", nil) 26 return 27 } 28 29 c.Redirect(setting.AppSubUrl + "/") 30} 31 32func notAuthorized(c *models.ReqContext) { 33 if c.IsApiRequest() { 34 c.JsonApiErr(401, "Unauthorized", nil) 35 return 36 } 37 38 writeRedirectCookie(c) 39 c.Redirect(setting.AppSubUrl + "/login") 40} 41 42func tokenRevoked(c *models.ReqContext, err *models.TokenRevokedError) { 43 if c.IsApiRequest() { 44 c.JSON(401, map[string]interface{}{ 45 "message": "Token revoked", 46 "error": map[string]interface{}{ 47 "id": "ERR_TOKEN_REVOKED", 48 "maxConcurrentSessions": err.MaxConcurrentSessions, 49 }, 50 }) 51 return 52 } 53 54 writeRedirectCookie(c) 55 c.Redirect(setting.AppSubUrl + "/login") 56} 57 58func writeRedirectCookie(c *models.ReqContext) { 59 redirectTo := c.Req.RequestURI 60 if setting.AppSubUrl != "" && !strings.HasPrefix(redirectTo, setting.AppSubUrl) { 61 redirectTo = setting.AppSubUrl + c.Req.RequestURI 62 } 63 64 // remove any forceLogin=true params 65 redirectTo = removeForceLoginParams(redirectTo) 66 67 cookies.WriteCookie(c.Resp, "redirect_to", url.QueryEscape(redirectTo), 0, nil) 68} 69 70var forceLoginParamsRegexp = regexp.MustCompile(`&?forceLogin=true`) 71 72func removeForceLoginParams(str string) string { 73 return forceLoginParamsRegexp.ReplaceAllString(str, "") 74} 75 76func EnsureEditorOrViewerCanEdit(c *models.ReqContext) { 77 if !c.SignedInUser.HasRole(models.ROLE_EDITOR) && !setting.ViewersCanEdit { 78 accessForbidden(c) 79 } 80} 81 82func RoleAuth(roles ...models.RoleType) web.Handler { 83 return func(c *models.ReqContext) { 84 ok := false 85 for _, role := range roles { 86 if role == c.OrgRole { 87 ok = true 88 break 89 } 90 } 91 if !ok { 92 accessForbidden(c) 93 } 94 } 95} 96 97func Auth(options *AuthOptions) web.Handler { 98 return func(c *models.ReqContext) { 99 forceLogin := false 100 if c.AllowAnonymous { 101 forceLogin = shouldForceLogin(c) 102 if !forceLogin { 103 orgIDValue := c.Req.URL.Query().Get("orgId") 104 orgID, err := strconv.ParseInt(orgIDValue, 10, 64) 105 if err == nil && orgID > 0 && orgID != c.OrgId { 106 forceLogin = true 107 } 108 } 109 } 110 111 requireLogin := !c.AllowAnonymous || forceLogin || options.ReqNoAnonynmous 112 113 if !c.IsSignedIn && options.ReqSignedIn && requireLogin { 114 var revokedErr *models.TokenRevokedError 115 if errors.As(c.LookupTokenErr, &revokedErr) { 116 tokenRevoked(c, revokedErr) 117 return 118 } 119 120 notAuthorized(c) 121 return 122 } 123 124 if !c.IsGrafanaAdmin && options.ReqGrafanaAdmin { 125 accessForbidden(c) 126 return 127 } 128 } 129} 130 131// AdminOrEditorAndFeatureEnabled creates a middleware that allows 132// access if the signed in user is either an Org Admin or if they 133// are an Org Editor and the feature flag is enabled. 134// Intended for when feature flags open up access to APIs that 135// are otherwise only available to admins. 136func AdminOrEditorAndFeatureEnabled(enabled bool) web.Handler { 137 return func(c *models.ReqContext) { 138 if c.OrgRole == models.ROLE_ADMIN { 139 return 140 } 141 142 if c.OrgRole == models.ROLE_EDITOR && enabled { 143 return 144 } 145 146 accessForbidden(c) 147 } 148} 149 150// SnapshotPublicModeOrSignedIn creates a middleware that allows access 151// if snapshot public mode is enabled or if user is signed in. 152func SnapshotPublicModeOrSignedIn(cfg *setting.Cfg) web.Handler { 153 return func(c *models.ReqContext) { 154 if cfg.SnapshotPublicMode { 155 return 156 } 157 158 if !c.IsSignedIn { 159 notAuthorized(c) 160 return 161 } 162 } 163} 164 165func ReqNotSignedIn(c *models.ReqContext) { 166 if c.IsSignedIn { 167 c.Redirect(setting.AppSubUrl + "/") 168 } 169} 170 171// NoAuth creates a middleware that doesn't require any authentication. 172// If forceLogin param is set it will redirect the user to the login page. 173func NoAuth() web.Handler { 174 return func(c *models.ReqContext) { 175 if shouldForceLogin(c) { 176 notAuthorized(c) 177 return 178 } 179 } 180} 181 182// shouldForceLogin checks if user should be enforced to login. 183// Returns true if forceLogin parameter is set. 184func shouldForceLogin(c *models.ReqContext) bool { 185 forceLogin := false 186 forceLoginParam, err := strconv.ParseBool(c.Req.URL.Query().Get("forceLogin")) 187 if err == nil { 188 forceLogin = forceLoginParam 189 } 190 191 return forceLogin 192} 193 194func OrgAdminFolderAdminOrTeamAdmin(c *models.ReqContext) { 195 if c.OrgRole == models.ROLE_ADMIN { 196 return 197 } 198 199 hasAdminPermissionInFoldersQuery := models.HasAdminPermissionInFoldersQuery{SignedInUser: c.SignedInUser} 200 if err := sqlstore.HasAdminPermissionInFolders(c.Req.Context(), &hasAdminPermissionInFoldersQuery); err != nil { 201 c.JsonApiErr(500, "Failed to check if user is a folder admin", err) 202 } 203 204 if hasAdminPermissionInFoldersQuery.Result { 205 return 206 } 207 208 isAdminOfTeamsQuery := models.IsAdminOfTeamsQuery{SignedInUser: c.SignedInUser} 209 if err := sqlstore.IsAdminOfTeams(c.Req.Context(), &isAdminOfTeamsQuery); err != nil { 210 c.JsonApiErr(500, "Failed to check if user is a team admin", err) 211 } 212 213 if isAdminOfTeamsQuery.Result { 214 return 215 } 216 217 accessForbidden(c) 218} 219