Identity: remove GetTypedID (#91745)

This commit is contained in:
Karl Persson 2024-08-09 17:20:24 +02:00 committed by GitHub
parent 6061fdc8b9
commit bcfb66b416
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
47 changed files with 211 additions and 386 deletions

View File

@ -43,13 +43,11 @@ func (hs *HTTPServer) isDashboardStarredByUser(c *contextmodel.ReqContext, dashI
return false, nil
}
namespaceID, userIDstr := c.SignedInUser.GetTypedID()
if namespaceID != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return false, nil
}
userID, err := identity.IntIdentifier(namespaceID, userIDstr)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return false, err
}
@ -439,16 +437,13 @@ func (hs *HTTPServer) deleteDashboard(c *contextmodel.ReqContext) response.Respo
return response.Error(http.StatusBadRequest, "Use folders endpoint for deleting folders.", nil)
}
namespaceID, userIDStr := c.SignedInUser.GetTypedID()
// disconnect all library elements for this dashboard
err = hs.LibraryElementService.DisconnectElementsFromDashboard(c.Req.Context(), dash.ID)
if err != nil {
hs.log.Error(
"Failed to disconnect library elements",
"dashboard", dash.ID,
"namespaceID", namespaceID,
"user", userIDStr,
"identity", c.GetID(),
"error", err)
}
@ -515,15 +510,9 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
ctx := c.Req.Context()
var err error
userID := int64(0)
namespaceID, userIDstr := c.SignedInUser.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
hs.log.Debug("User does not belong to a user or service account namespace", "namespaceID", namespaceID, "userID", userIDstr)
} else {
userID, err = identity.IntIdentifier(namespaceID, userIDstr)
if err != nil {
hs.log.Debug("Error while parsing user ID", "namespaceID", namespaceID, "userID", userIDstr)
}
var userID int64
if id, err := identity.UserIdentifier(c.SignedInUser.GetID()); err == nil {
userID = id
}
cmd.OrgID = c.SignedInUser.GetOrgID()
@ -632,16 +621,9 @@ func (hs *HTTPServer) postDashboard(c *contextmodel.ReqContext, cmd dashboards.S
// 401: unauthorisedError
// 500: internalServerError
func (hs *HTTPServer) GetHomeDashboard(c *contextmodel.ReqContext) response.Response {
userID := int64(0)
var err error
namespaceID, userIDstr := c.SignedInUser.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
hs.log.Debug("User does not belong to a user or service account namespace", "namespaceID", namespaceID, "userID", userIDstr)
} else {
userID, err = identity.IntIdentifier(namespaceID, userIDstr)
if err != nil {
hs.log.Debug("Error while parsing user ID", "namespaceID", namespaceID, "userID", userIDstr)
}
var userID int64
if id, err := identity.UserIdentifier(c.SignedInUser.GetID()); err == nil {
userID = id
}
prefsQuery := pref.GetPreferenceWithDefaultsQuery{OrgID: c.SignedInUser.GetOrgID(), UserID: userID, Teams: c.SignedInUser.GetTeams()}
@ -1085,15 +1067,9 @@ func (hs *HTTPServer) RestoreDashboardVersion(c *contextmodel.ReqContext) respon
return response.Error(http.StatusNotFound, "Dashboard version not found", nil)
}
userID := int64(0)
namespaceID, userIDstr := c.SignedInUser.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
hs.log.Warn("User does not belong to a user or service account namespace", "namespaceID", namespaceID, "userID", userIDstr)
} else {
userID, err = identity.IntIdentifier(namespaceID, userIDstr)
if err != nil {
hs.log.Warn("Error while parsing user ID", "namespaceID", namespaceID, "userID", userIDstr)
}
var userID int64
if id, err := identity.UserIdentifier(c.SignedInUser.GetID()); err == nil {
userID = id
}
saveCmd := dashboards.SaveDashboardCommand{}

View File

@ -442,11 +442,7 @@ func (hs *HTTPServer) AddDataSource(c *contextmodel.ReqContext) response.Respons
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID, err := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if err != nil {
return response.Error(http.StatusInternalServerError,
"Failed to add datasource", err)
}
userID, _ := identity.UserIdentifier(c.SignedInUser.GetID())
datasourcesLogger.Debug("Received command to add data source", "url", cmd.URL)
cmd.OrgID = c.SignedInUser.GetOrgID()

View File

@ -191,15 +191,13 @@ func (hs *HTTPServer) setDefaultFolderPermissions(ctx context.Context, orgID int
if !hs.Cfg.RBAC.PermissionsOnCreation("folder") {
return nil
}
var permissions []accesscontrol.SetResourcePermissionCommand
var userID int64
namespace, id := user.GetTypedID()
if namespace == identity.TypeUser {
var errID error
userID, errID = identity.IntIdentifier(namespace, id)
if errID != nil {
return errID
var permissions []accesscontrol.SetResourcePermissionCommand
if identity.IsIdentityType(user.GetID(), identity.TypeUser) {
userID, err := identity.UserIdentifier(user.GetID())
if err != nil {
return err
}
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{

View File

@ -31,7 +31,7 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
return nil, err
}
userID, _ := identity.UserIdentifier(c.SignedInUser.GetTypedID())
userID, _ := identity.UserIdentifier(c.SignedInUser.GetID())
prefsQuery := pref.GetPreferenceWithDefaultsQuery{UserID: userID, OrgID: c.SignedInUser.GetOrgID(), Teams: c.Teams}
prefs, err := hs.preferenceService.GetWithDefaults(c.Req.Context(), &prefsQuery)
@ -169,10 +169,8 @@ func (hs *HTTPServer) setIndexViewData(c *contextmodel.ReqContext) (*dtos.IndexV
}
func (hs *HTTPServer) buildUserAnalyticsSettings(c *contextmodel.ReqContext) dtos.AnalyticsSettings {
namespace, _ := c.SignedInUser.GetTypedID()
// Anonymous users do not have an email or auth info
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return dtos.AnalyticsSettings{Identifier: "@" + hs.Cfg.AppURL}
}

View File

@ -257,8 +257,7 @@ func (hs *HTTPServer) Logout(c *contextmodel.ReqContext) {
return
}
_, id := c.SignedInUser.GetTypedID()
hs.log.Info("Successful Logout", "userID", id)
hs.log.Info("Successful Logout", "id", c.SignedInUser.GetID())
c.Redirect(redirect.URL)
}
@ -305,7 +304,7 @@ func (hs *HTTPServer) redirectURLWithErrorCookie(c *contextmodel.ReqContext, err
var userID int64
if c.SignedInUser != nil && !c.SignedInUser.IsNil() {
var errID error
userID, errID = identity.UserIdentifier(c.SignedInUser.GetTypedID())
userID, errID = identity.UserIdentifier(c.SignedInUser.GetID())
if errID != nil {
hs.log.Error("failed to retrieve user ID", "error", errID)
}

View File

@ -132,12 +132,11 @@ func (hs *HTTPServer) CreateOrg(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "bad request data", err)
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return response.Error(http.StatusForbidden, "Only users can create organizations", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
}

View File

@ -101,14 +101,9 @@ func (hs *HTTPServer) AddOrgInvite(c *contextmodel.ReqContext) response.Response
cmd.Name = inviteDto.Name
cmd.Status = tempuser.TmpUserInvitePending
namespace, identifier := c.SignedInUser.GetTypedID()
var userID int64
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
var err error
userID, err = strconv.ParseInt(identifier, 10, 64)
if err != nil {
return response.Error(http.StatusInternalServerError, "Unrecognized user", err)
}
if id, err := identity.UserIdentifier(c.SignedInUser.GetID()); err == nil {
userID = id
}
cmd.InvitedByUserID = userID

View File

@ -22,9 +22,9 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
return response.Error(http.StatusInternalServerError, "Failed to set home dashboard", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to set home dashboard", err)
}
cmd.UserID = userID
@ -64,9 +64,9 @@ func (hs *HTTPServer) SetHomeDashboard(c *contextmodel.ReqContext) response.Resp
// 401: unauthorisedError
// 500: internalServerError
func (hs *HTTPServer) GetUserPreferences(c *contextmodel.ReqContext) response.Response {
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
return response.Error(http.StatusInternalServerError, "Failed to get user preferences", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to update user preferences", err)
}
return prefapi.GetPreferencesFor(c.Req.Context(), hs.DashboardService, hs.preferenceService, c.SignedInUser.GetOrgID(), userID, 0)
@ -89,9 +89,9 @@ func (hs *HTTPServer) UpdateUserPreferences(c *contextmodel.ReqContext) response
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
return response.Error(http.StatusInternalServerError, "Failed to update user preferences", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to update user preferences", err)
}
return prefapi.UpdatePreferencesFor(c.Req.Context(), hs.DashboardService,
@ -113,9 +113,9 @@ func (hs *HTTPServer) PatchUserPreferences(c *contextmodel.ReqContext) response.
return response.Error(http.StatusBadRequest, "bad request data", err)
}
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
return response.Error(http.StatusInternalServerError, "Failed to update user preferences", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to update user preferences", err)
}
return hs.patchPreferencesFor(c.Req.Context(), c.SignedInUser.GetOrgID(), userID, 0, &dtoCmd)

View File

@ -65,9 +65,9 @@ func (hs *HTTPServer) RenderHandler(c *contextmodel.ReqContext) {
headers["Accept-Language"] = acceptLanguageHeader
}
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
hs.log.Error("Failed to parse user id", "err", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
hs.log.Debug("Failed to parse user id", "err", err)
}
encoding := queryReader.Get("encoding", "")

View File

@ -47,9 +47,9 @@ func (hs *HTTPServer) SignUp(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusUnprocessableEntity, "User with same email address already exists", nil)
}
userID, errID := identity.UserIdentifier(c.SignedInUser.GetTypedID())
if errID != nil {
hs.log.Error("Failed to parse user id", "err", errID)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
hs.log.Debug("Failed to parse user id", "err", err)
}
cmd := tempuser.CreateTempUserCommand{}

View File

@ -31,8 +31,7 @@ import (
// 404: notFoundError
// 500: internalServerError
func (hs *HTTPServer) GetSignedInUser(c *contextmodel.ReqContext) response.Response {
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.GetID(), identity.TypeUser) {
return response.JSON(http.StatusOK, user.UserProfileDTO{
IsGrafanaAdmin: c.SignedInUser.GetIsGrafanaAdmin(),
OrgID: c.SignedInUser.GetOrgID(),
@ -43,7 +42,7 @@ func (hs *HTTPServer) GetSignedInUser(c *contextmodel.ReqContext) response.Respo
})
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
}
@ -278,8 +277,7 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
}
func (hs *HTTPServer) StartEmailVerificaton(c *contextmodel.ReqContext) response.Response {
namespace, id := c.SignedInUser.GetTypedID()
if !identity.IsIdentityType(namespace, identity.TypeUser) {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return response.Error(http.StatusBadRequest, "Only users can verify their email", nil)
}
@ -288,7 +286,7 @@ func (hs *HTTPServer) StartEmailVerificaton(c *contextmodel.ReqContext) response
return response.Respond(http.StatusNotModified, nil)
}
userID, err := strconv.ParseInt(id, 10, 64)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Got invalid user id", err)
}
@ -506,13 +504,12 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *contextmodel.ReqContex
return
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
c.JsonApiErr(http.StatusForbidden, "Endpoint only available for users", nil)
return
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
c.JsonApiErr(http.StatusInternalServerError, "Failed to parse user id", err)
return
@ -632,12 +629,11 @@ func (hs *HTTPServer) ClearHelpFlags(c *contextmodel.ReqContext) response.Respon
}
func getUserID(c *contextmodel.ReqContext) (int64, *response.NormalResponse) {
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return 0, response.Error(http.StatusForbidden, "Endpoint only available for users", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return 0, response.Error(http.StatusInternalServerError, "Failed to parse user id", err)
}

View File

@ -32,12 +32,11 @@ import (
// 403: forbiddenError
// 500: internalServerError
func (hs *HTTPServer) GetUserAuthTokens(c *contextmodel.ReqContext) response.Response {
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
return response.Error(http.StatusForbidden, "entity not allowed to revoke tokens", nil)
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return response.Error(http.StatusForbidden, "entity not allowed to get tokens", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "failed to parse user id", err)
}
@ -63,12 +62,11 @@ func (hs *HTTPServer) RevokeUserAuthToken(c *contextmodel.ReqContext) response.R
return response.Error(http.StatusBadRequest, "bad request data", err)
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser {
if !identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
return response.Error(http.StatusForbidden, "entity not allowed to revoke tokens", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "failed to parse user id", err)
}

View File

@ -7,6 +7,7 @@ import (
)
var (
ErrInvalidIDType = errutil.BadRequest("auth.identity.invalid-id-type")
ErrInvalidTypedID = errutil.BadRequest("auth.identity.invalid-typed-id")
ErrNotIntIdentifier = errors.New("identifier is not an int64")
ErrIdentifierNotInitialized = errors.New("identifier is not initialized")

View File

@ -17,15 +17,9 @@ type Requester interface {
GetRawIdentifier() string
// Deprecated: use GetUID instead
GetInternalID() (int64, error)
// GetID returns namespaced internalID for the entity
// Deprecated: use GetUID instead
GetID() TypedID
// GetTypedID returns the namespace and ID of the active entity.
// The namespace is one of the constants defined in pkg/apimachinery/identity.
// Deprecated: use GetID instead
GetTypedID() (kind IdentityType, identifier string)
// GetDisplayName returns the display name of the active entity.
// The display name is the name if it is set, otherwise the login or email.
GetDisplayName() string
@ -82,14 +76,14 @@ type Requester interface {
GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims]
}
// IntIdentifier converts a string identifier to an int64.
// IntIdentifier converts a typeID to an int64.
// Applicable for users, service accounts, api keys and renderer service.
// Errors if the identifier is not initialized or if namespace is not recognized.
func IntIdentifier(kind IdentityType, identifier string) (int64, error) {
if IsIdentityType(kind, TypeUser, TypeAPIKey, TypeServiceAccount, TypeRenderService) {
id, err := strconv.ParseInt(identifier, 10, 64)
// Errors if the identifier is not initialized or if type is not recognized.
func IntIdentifier(typedID TypedID) (int64, error) {
if IsIdentityType(typedID, TypeUser, TypeAPIKey, TypeServiceAccount, TypeRenderService) {
id, err := strconv.ParseInt(typedID.ID(), 10, 64)
if err != nil {
return 0, fmt.Errorf("unrecognized format for valid type %s: %w", kind, err)
return 0, fmt.Errorf("unrecognized format for valid type %s: %w", typedID.Type(), err)
}
if id < 1 {
@ -102,19 +96,18 @@ func IntIdentifier(kind IdentityType, identifier string) (int64, error) {
return 0, ErrNotIntIdentifier
}
// UserIdentifier converts a string identifier to an int64.
// UserIdentifier converts a typeID to an int64.
// Errors if the identifier is not initialized or if namespace is not recognized.
// Returns 0 if the namespace is not user or service account
func UserIdentifier(kind IdentityType, identifier string) (int64, error) {
userID, err := IntIdentifier(kind, identifier)
// Returns 0 if the type is not user or service account
func UserIdentifier(typedID TypedID) (int64, error) {
userID, err := IntIdentifier(typedID)
if err != nil {
// FIXME: return this error once entity namespaces are handled by stores
return 0, nil
return 0, err
}
if IsIdentityType(kind, TypeUser, TypeServiceAccount) {
if IsIdentityType(typedID, TypeUser, TypeServiceAccount) {
return userID, nil
}
return 0, nil
return 0, ErrInvalidIDType
}

View File

@ -149,12 +149,6 @@ func (u *StaticRequester) GetID() TypedID {
return NewTypedIDString(u.Type, fmt.Sprintf("%d", u.UserID))
}
// GetTypedID returns the namespace and ID of the active entity
// The namespace is one of the constants defined in pkg/apimachinery/identity
func (u *StaticRequester) GetTypedID() (IdentityType, string) {
return u.Type, fmt.Sprintf("%d", u.UserID)
}
func (u *StaticRequester) GetAuthID() string {
return u.AuthID
}

View File

@ -42,10 +42,10 @@ func ParseType(str string) (IdentityType, error) {
}
}
// IsIdentityType returns true if type matches any expected identity type
func IsIdentityType(typ IdentityType, expected ...IdentityType) bool {
// IsIdentityType returns true if typedID matches any expected identity type
func IsIdentityType(typedID TypedID, expected ...IdentityType) bool {
for _, e := range expected {
if typ == e {
if typedID.Type() == e {
return true
}
}
@ -128,7 +128,7 @@ func (ni TypedID) Type() IdentityType {
}
func (ni TypedID) IsType(expected ...IdentityType) bool {
return IsIdentityType(ni.t, expected...)
return IsIdentityType(ni, expected...)
}
func (ni TypedID) String() string {

View File

@ -192,6 +192,5 @@ func (a *AccessControl) RegisterScopeAttributeResolver(prefix string, resolver a
}
func (a *AccessControl) debug(ctx context.Context, ident identity.Requester, msg string, eval accesscontrol.Evaluator) {
namespace, id := ident.GetTypedID()
a.log.FromContext(ctx).Debug(msg, "namespace", namespace, "id", id, "orgID", ident.GetOrgID(), "permissions", eval.GoString())
a.log.FromContext(ctx).Debug(msg, "id", ident.GetID(), "orgID", ident.GetOrgID(), "permissions", eval.GoString())
}

View File

@ -128,7 +128,7 @@ func (s *Service) GetUserPermissions(ctx context.Context, user identity.Requeste
return s.getCachedUserPermissions(ctx, user, options)
}
func (s *Service) getUserPermissions(ctx context.Context, user identity.Requester, options accesscontrol.Options) ([]accesscontrol.Permission, error) {
func (s *Service) getUserPermissions(ctx context.Context, user identity.Requester, _ accesscontrol.Options) ([]accesscontrol.Permission, error) {
ctx, span := s.tracer.Start(ctx, "authz.getUserPermissions")
defer span.End()
@ -143,10 +143,9 @@ func (s *Service) getUserPermissions(ctx context.Context, user identity.Requeste
permissions = append(permissions, SharedWithMeFolderPermission)
}
userID, err := identity.UserIdentifier(user.GetTypedID())
if err != nil {
return nil, err
}
// we don't care about the error here, if this fails we get 0 and no
// permission assigned to user will be returned, only for org role.
userID, _ := identity.UserIdentifier(user.GetID())
dbPermissions, err := s.store.GetUserPermissions(ctx, accesscontrol.GetUserPermissionsQuery{
OrgID: user.GetOrgID(),
@ -211,12 +210,10 @@ func (s *Service) getUserDirectPermissions(ctx context.Context, user identity.Re
ctx, span := s.tracer.Start(ctx, "authz.getUserDirectPermissions")
defer span.End()
namespace, identifier := user.GetTypedID()
var userID int64
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
if identity.IsIdentityType(user.GetID(), identity.TypeUser, identity.TypeServiceAccount) {
var err error
userID, err = strconv.ParseInt(identifier, 10, 64)
userID, err = identity.UserIdentifier(user.GetID())
if err != nil {
return nil, err
}

View File

@ -80,11 +80,9 @@ func deny(c *contextmodel.ReqContext, evaluator Evaluator, err error) {
if err != nil {
c.Logger.Error("Error from access control system", "error", err, "accessErrorID", id)
} else {
namespace, identifier := c.SignedInUser.GetTypedID()
c.Logger.Info(
"Access denied",
"namespace", namespace,
"userID", identifier,
"id", c.SignedInUser.GetID(),
"accessErrorID", id,
"permissions", evaluator.GoString(),
)

View File

@ -70,12 +70,10 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
idClaims *auth.IDClaims
}
result, err, _ := s.si.Do(cacheKey, func() (any, error) {
namespace, identifier := id.GetTypedID()
cachedToken, err := s.cache.Get(ctx, cacheKey)
if err == nil {
s.metrics.tokenSigningFromCacheCounter.Inc()
s.logger.FromContext(ctx).Debug("Cached token found", "namespace", namespace, "id", identifier)
s.logger.FromContext(ctx).Debug("Cached token found", "id", id.GetID())
tokenClaims, err := s.extractTokenClaims(string(cachedToken))
if err != nil {
@ -85,14 +83,14 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
}
s.metrics.tokenSigningCounter.Inc()
s.logger.FromContext(ctx).Debug("Sign new id token", "namespace", namespace, "id", identifier)
s.logger.FromContext(ctx).Debug("Sign new id token", "id", id.GetID())
now := time.Now()
claims := &auth.IDClaims{
Claims: &jwt.Claims{
Issuer: s.cfg.AppURL,
Audience: getAudience(id.GetOrgID()),
Subject: getSubject(namespace.String(), identifier),
Subject: id.GetID().String(),
Expiry: jwt.NewNumericDate(now.Add(tokenTTL)),
IssuedAt: jwt.NewNumericDate(now),
},
@ -101,7 +99,7 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri
},
}
if identity.IsIdentityType(namespace, identity.TypeUser) {
if identity.IsIdentityType(id.GetID(), identity.TypeUser) {
claims.Rest.Email = id.GetEmail()
claims.Rest.EmailVerified = id.IsEmailVerified()
claims.Rest.AuthenticatedBy = id.GetAuthenticatedBy()
@ -145,8 +143,7 @@ func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.R
token, claims, err := s.SignIdentity(ctx, identity)
if err != nil {
if shouldLogErr(err) {
namespace, id := identity.GetTypedID()
s.logger.FromContext(ctx).Error("Failed to sign id token", "err", err, "namespace", namespace, "id", id)
s.logger.FromContext(ctx).Error("Failed to sign id token", "err", err, "id", identity.GetID())
}
// for now don't return error so we don't break authentication from this hook
return nil
@ -179,10 +176,6 @@ func getAudience(orgID int64) jwt.Audience {
return jwt.Audience{fmt.Sprintf("org:%d", orgID)}
}
func getSubject(namespace, identifier string) string {
return fmt.Sprintf("%s:%s", namespace, identifier)
}
func prefixCacheKey(key string) string {
return fmt.Sprintf("%s-%s", cachePrefix, key)
}

View File

@ -248,10 +248,9 @@ func (c *OAuth) RedirectURL(ctx context.Context, r *authn.Request) (*authn.Redir
func (c *OAuth) Logout(ctx context.Context, user identity.Requester) (*authn.Redirect, bool) {
token := c.oauthService.GetCurrentOAuthToken(ctx, user)
namespace, id := user.GetTypedID()
userID, err := identity.UserIdentifier(namespace, id)
userID, err := identity.UserIdentifier(user.GetID())
if err != nil {
c.log.FromContext(ctx).Error("Failed to parse user id", "namespace", namespace, "id", id, "error", err)
c.log.FromContext(ctx).Error("Failed to parse user id", "id", user.GetID(), "error", err)
return nil, false
}
@ -260,8 +259,7 @@ func (c *OAuth) Logout(ctx context.Context, user identity.Requester) (*authn.Red
AuthId: user.GetAuthID(),
AuthModule: user.GetAuthenticatedBy(),
}); err != nil {
namespace, id := user.GetTypedID()
c.log.FromContext(ctx).Error("Failed to invalidate tokens", "namespace", namespace, "id", id, "error", err)
c.log.FromContext(ctx).Error("Failed to invalidate tokens", "id", user.GetID(), "error", err)
}
oauthCfg := c.socialService.GetOAuthInfoProvider(c.providerName)

View File

@ -485,7 +485,7 @@ func TestOAuth_Logout(t *testing.T) {
}
c := ProvideOAuth(authn.ClientWithPrefix("azuread"), tt.cfg, mockService, fakeSocialSvc, &setting.OSSImpl{Cfg: tt.cfg}, featuremgmt.WithFeatures())
redirect, ok := c.Logout(context.Background(), &authn.Identity{})
redirect, ok := c.Logout(context.Background(), &authn.Identity{ID: identity.NewTypedIDString(identity.TypeUser, "1")})
assert.Equal(t, tt.expectedOK, ok)
if tt.expectedOK {

View File

@ -115,10 +115,6 @@ func (i *Identity) GetID() identity.TypedID {
return i.ID
}
func (i *Identity) GetTypedID() (namespace identity.IdentityType, identifier string) {
return i.ID.Type(), i.ID.ID()
}
func (i *Identity) GetUID() string {
return i.UID.String()
}
@ -132,14 +128,14 @@ func (i *Identity) GetAuthenticatedBy() string {
}
func (i *Identity) GetCacheKey() string {
namespace, id := i.GetTypedID()
id := i.GetID().ID()
if !i.HasUniqueId() {
// Hack use the org role as id for identities that do not have a unique id
// e.g. anonymous and render key.
id = string(i.GetOrgRole())
}
return fmt.Sprintf("%d-%s-%s", i.GetOrgID(), namespace, id)
return fmt.Sprintf("%d-%s-%s", i.GetOrgID(), i.GetID().Type(), id)
}
func (i *Identity) GetDisplayName() string {
@ -232,10 +228,10 @@ func (i *Identity) HasRole(role org.RoleType) bool {
}
func (i *Identity) HasUniqueId() bool {
namespace, _ := i.GetTypedID()
return namespace == identity.TypeUser ||
namespace == identity.TypeServiceAccount ||
namespace == identity.TypeAPIKey
typ := i.GetID().Type()
return typ == identity.TypeUser ||
typ == identity.TypeServiceAccount ||
typ == identity.TypeAPIKey
}
func (i *Identity) IsAuthenticatedBy(providers ...string) bool {

View File

@ -154,22 +154,22 @@ func (h *ContextHandler) addIDHeaderEndOfRequestFunc(ident identity.Requester) w
return
}
namespace, id := ident.GetTypedID()
id := ident.GetID()
if !identity.IsIdentityType(
namespace,
id,
identity.TypeUser,
identity.TypeServiceAccount,
identity.TypeAPIKey,
) || id == "0" {
) || id.ID() == "0" {
return
}
if _, ok := h.Cfg.IDResponseHeaderNamespaces[namespace.String()]; !ok {
if _, ok := h.Cfg.IDResponseHeaderNamespaces[id.Type().String()]; !ok {
return
}
headerName := fmt.Sprintf("%s-Identity-Id", h.Cfg.IDResponseHeaderPrefix)
w.Header().Add(headerName, fmt.Sprintf("%s:%s", namespace, id))
w.Header().Add(headerName, id.String())
}
}

View File

@ -109,11 +109,9 @@ func (s *ImportDashboardService) ImportDashboard(ctx context.Context, req *dashb
req.FolderUid = folder.UID
}
namespace, identifier := req.User.GetTypedID()
userID := int64(0)
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
userID, _ = identity.IntIdentifier(namespace, identifier)
var userID int64
if id, err := identity.UserIdentifier(req.User.GetID()); err == nil {
userID = id
}
saveCmd := dashboards.SaveDashboardCommand{

View File

@ -83,7 +83,7 @@ func TestImportDashboardService(t *testing.T) {
require.NotNil(t, resp)
require.Equal(t, "UDdpyzz7z", resp.UID)
userID, err := identity.IntIdentifier(importDashboardArg.User.GetTypedID())
userID, err := identity.IntIdentifier(importDashboardArg.User.GetID())
require.NoError(t, err)
require.NotNil(t, importDashboardArg)
@ -149,7 +149,7 @@ func TestImportDashboardService(t *testing.T) {
require.NotNil(t, resp)
require.Equal(t, "UDdpyzz7z", resp.UID)
userID, err := identity.IntIdentifier(importDashboardArg.User.GetTypedID())
userID, err := identity.IntIdentifier(importDashboardArg.User.GetID())
require.NoError(t, err)
require.NotNil(t, importDashboardArg)

View File

@ -207,9 +207,11 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
}
}
userID, err := resolveUserID(dto.User, dr.log)
if err != nil {
return nil, err
var userID int64
if id, err := identity.UserIdentifier(dto.User.GetID()); err == nil {
userID = id
} else {
dr.log.Debug("User does not belong to a user or service account namespace, using 0 as user ID", "id", dto.User.GetID())
}
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Dashboard).Inc()
@ -232,22 +234,6 @@ func (dr *DashboardServiceImpl) BuildSaveDashboardCommand(ctx context.Context, d
return cmd, nil
}
func resolveUserID(user identity.Requester, log log.Logger) (int64, error) {
userID := int64(0)
namespaceID, identifier := user.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
log.Debug("User does not belong to a user or service account namespace", "namespaceID", namespaceID, "userID", identifier)
} else {
var err error
userID, err = identity.IntIdentifier(namespaceID, identifier)
if err != nil {
log.Debug("failed to parse user ID", "namespaceID", namespaceID, "userID", identifier, "error", err)
}
}
return userID, nil
}
func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
return dr.dashboardStore.DeleteOrphanedProvisionedDashboards(ctx, cmd)
}
@ -503,12 +489,10 @@ func (dr *DashboardServiceImpl) setDefaultPermissions(ctx context.Context, dto *
var permissions []accesscontrol.SetResourcePermissionCommand
if !provisioned {
namespaceID, userIDstr := dto.User.GetTypedID()
userID, err := identity.IntIdentifier(namespaceID, userIDstr)
userID, err := identity.IntIdentifier(dto.User.GetID())
if err != nil {
dr.log.Error("Could not make user admin", "dashboard", dash.Title, "namespaceID", namespaceID, "userID", userID, "error", err)
} else if namespaceID == identity.TypeUser && userID > 0 {
dr.log.Error("Could not make user admin", "dashboard", dash.Title, "id", dto.User.GetID(), "error", err)
} else if identity.IsIdentityType(dto.User.GetID(), identity.TypeUser) {
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(),
})
@ -541,12 +525,10 @@ func (dr *DashboardServiceImpl) setDefaultFolderPermissions(ctx context.Context,
var permissions []accesscontrol.SetResourcePermissionCommand
if !provisioned {
namespaceID, userIDstr := cmd.SignedInUser.GetTypedID()
userID, err := identity.IntIdentifier(namespaceID, userIDstr)
userID, err := identity.IntIdentifier(cmd.SignedInUser.GetID())
if err != nil {
dr.log.Error("Could not make user admin", "folder", cmd.Title, "namespaceID", namespaceID, "userID", userID, "error", err)
} else if namespaceID == identity.TypeUser && userID > 0 {
dr.log.Error("Could not make user admin", "folder", cmd.Title, "id", cmd.SignedInUser.GetID())
} else if identity.IsIdentityType(cmd.SignedInUser.GetID(), identity.TypeUser) {
permissions = append(permissions, accesscontrol.SetResourcePermissionCommand{
UserID: userID, Permission: dashboardaccess.PERMISSION_ADMIN.String(),
})

View File

@ -116,7 +116,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, "", sc.dashboardGuardianMock.DashUID)
@ -139,7 +139,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.otherSavedFolder.ID, sc.dashboardGuardianMock.DashID)
@ -162,7 +162,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -186,7 +186,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -210,7 +210,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInGeneralFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -234,7 +234,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -258,7 +258,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInGeneralFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -282,7 +282,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
assert.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -306,7 +306,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInGeneralFolder.UID, sc.dashboardGuardianMock.DashUID)
@ -330,7 +330,7 @@ func TestIntegrationIntegratedDashboardService(t *testing.T) {
err := callSaveWithError(t, cmd, sc.sqlStore)
require.Equal(t, dashboards.ErrDashboardUpdateAccessDenied, err)
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetTypedID())
userID, err := identity.IntIdentifier(sc.dashboardGuardianMock.User.GetID())
require.NoError(t, err)
assert.Equal(t, sc.savedDashInFolder.UID, sc.dashboardGuardianMock.DashUID)

View File

@ -122,12 +122,10 @@ func (d *DashboardSnapshotStore) SearchDashboardSnapshots(ctx context.Context, q
sess.Where("name LIKE ?", query.Name)
}
namespace, id := query.SignedInUser.GetTypedID()
var userID int64
if namespace == identity.TypeServiceAccount || namespace == identity.TypeUser {
if identity.IsIdentityType(query.SignedInUser.GetID(), identity.TypeUser, identity.TypeServiceAccount) {
var err error
userID, err = identity.IntIdentifier(namespace, id)
userID, err = identity.UserIdentifier(query.SignedInUser.GetID())
if err != nil {
return err
}
@ -137,7 +135,7 @@ func (d *DashboardSnapshotStore) SearchDashboardSnapshots(ctx context.Context, q
switch {
case query.SignedInUser.GetOrgRole() == org.RoleAdmin:
sess.Where("org_id = ?", query.SignedInUser.GetOrgID())
case namespace != identity.TypeAnonymous:
case userID != 0:
sess.Where("org_id = ? AND user_id = ?", query.OrgID, userID)
default:
queryResult = snapshots

View File

@ -63,17 +63,10 @@ func CreateDashboardSnapshot(c *contextmodel.ReqContext, cfg dashboardsnapshot.S
cmd.DashboardCreateCommand.Name = "Unnamed snapshot"
}
userID, err := identity.UserIdentifier(user.GetTypedID())
if err != nil {
c.JsonApiErr(http.StatusInternalServerError,
"Failed to create external snapshot", err)
return
}
var snapshotUrl string
cmd.ExternalURL = ""
cmd.OrgID = user.GetOrgID()
cmd.UserID = userID
cmd.UserID, _ = identity.UserIdentifier(user.GetID())
originalDashboardURL, err := createOriginalDashboardURL(&cmd)
if err != nil {
c.JsonApiErr(http.StatusInternalServerError, "Invalid app URL", err)

View File

@ -592,21 +592,17 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
user := cmd.SignedInUser
userID := int64(0)
var err error
namespaceID, userIDstr := user.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
s.log.Debug("User does not belong to a user or service account namespace, using 0 as user ID", "namespaceID", namespaceID, "userID", userIDstr)
var userID int64
if id, err := identity.UserIdentifier(cmd.SignedInUser.GetID()); err == nil {
userID = id
} else {
userID, err = identity.IntIdentifier(namespaceID, userIDstr)
if err != nil {
s.log.Debug("failed to parse user ID", "namespaceID", namespaceID, "userID", userIDstr, "error", err)
}
s.log.Warn("User does not belong to a user or service account namespace, using 0 as user ID", "id", cmd.SignedInUser.GetID())
}
if userID == 0 {
userID = -1
}
dashFolder.CreatedBy = userID
dashFolder.UpdatedBy = userID
dashFolder.UpdateSlug()
@ -682,8 +678,6 @@ func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (
}
if cmd.NewTitle != nil {
namespace, id := cmd.SignedInUser.GetTypedID()
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()
if err := s.bus.Publish(ctx, &events.FolderTitleUpdated{
Timestamp: foldr.Updated,
@ -692,7 +686,7 @@ func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (
UID: dashFolder.UID,
OrgID: cmd.OrgID,
}); err != nil {
s.log.ErrorContext(ctx, "failed to publish FolderTitleUpdated event", "folder", foldr.Title, "user", id, "namespace", namespace, "error", err)
s.log.ErrorContext(ctx, "failed to publish FolderTitleUpdated event", "folder", foldr.Title, "user", cmd.SignedInUser.GetID(), "error", err)
return err
}
}
@ -739,12 +733,10 @@ func (s *Service) legacyUpdate(ctx context.Context, cmd *folder.UpdateFolderComm
}
var userID int64
namespace, id := cmd.SignedInUser.GetTypedID()
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
userID, err = identity.IntIdentifier(namespace, id)
if err != nil {
s.log.ErrorContext(ctx, "failed to parse user ID", "namespace", namespace, "userID", id, "error", err)
}
if id, err := identity.UserIdentifier(cmd.SignedInUser.GetID()); err == nil {
userID = id
} else {
s.log.Warn("User does not belong to a user or service account namespace, using 0 as user ID", "id", cmd.SignedInUser.GetID())
}
prepareForUpdate(dashFolder, cmd.OrgID, userID, cmd)
@ -1163,15 +1155,11 @@ func (s *Service) buildSaveDashboardCommand(ctx context.Context, dto *dashboards
}
}
userID := int64(0)
namespaceID, userIDstr := dto.User.GetTypedID()
if namespaceID != identity.TypeUser && namespaceID != identity.TypeServiceAccount {
s.log.Warn("User does not belong to a user or service account namespace, using 0 as user ID", "namespaceID", namespaceID, "userID", userIDstr)
var userID int64
if id, err := identity.UserIdentifier(dto.User.GetID()); err == nil {
userID = id
} else {
userID, err = identity.IntIdentifier(namespaceID, userIDstr)
if err != nil {
s.log.Warn("failed to parse user ID", "namespaceID", namespaceID, "userID", userIDstr, "error", err)
}
s.log.Warn("User does not belong to a user or service account namespace, using 0 as user ID", "id", dto.User.GetID())
}
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Folder).Inc()

View File

@ -309,13 +309,12 @@ func (a *accessControlFolderGuardian) CanCreate(folderID int64, isFolder bool) (
func (a *accessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evaluator) (bool, error) {
ok, err := a.ac.Evaluate(a.ctx, a.user, evaluator)
namespaceID, userID := a.user.GetTypedID()
if err != nil {
id := 0
if a.dashboard != nil {
id = int(a.dashboard.ID)
}
a.log.Debug("Failed to evaluate access control to dashboard", "error", err, "namespaceID", namespaceID, "userId", userID, "id", id)
a.log.Debug("Failed to evaluate access control to dashboard", "error", err, "identity", a.user.GetID(), "id", id)
}
if !ok && err == nil {
@ -323,7 +322,7 @@ func (a *accessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evalua
if a.dashboard != nil {
id = int(a.dashboard.ID)
}
a.log.Debug("Access denied to dashboard", "namespaceID", namespaceID, "userId", userID, "id", id, "permissions", evaluator.GoString())
a.log.Debug("Access denied to dashboard", "identity", a.user.GetID(), "id", id, "permissions", evaluator.GoString())
}
return ok, err
@ -331,7 +330,6 @@ func (a *accessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evalua
func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator) (bool, error) {
ok, err := a.ac.Evaluate(a.ctx, a.user, evaluator)
namespaceID, userID := a.user.GetTypedID()
if err != nil {
uid := ""
orgID := 0
@ -339,7 +337,7 @@ func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator
uid = a.folder.UID
orgID = int(a.folder.OrgID)
}
a.log.Debug("Failed to evaluate access control to folder", "error", err, "namespaceID", namespaceID, "userId", userID, "orgID", orgID, "uid", uid)
a.log.Debug("Failed to evaluate access control to folder", "error", err, "identity", a.user.GetID(), "orgID", orgID, "uid", uid)
}
if !ok && err == nil {
@ -349,7 +347,7 @@ func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator
uid = a.folder.UID
orgID = int(a.folder.OrgID)
}
a.log.Debug("Access denied to folder", "namespaceID", namespaceID, "userId", userID, "orgID", orgID, "uid", uid, "permissions", evaluator.GoString())
a.log.Debug("Access denied to folder", "identity", a.user.GetID(), "identity", a.user.GetID(), "orgID", orgID, "uid", uid, "permissions", evaluator.GoString())
}
return ok, err

View File

@ -138,13 +138,9 @@ func (l *LibraryElementService) createLibraryElement(c context.Context, signedIn
}
}
userID := int64(0)
namespaceID, identifier := signedInUser.GetTypedID()
if namespaceID == identity.TypeUser || namespaceID == identity.TypeServiceAccount {
userID, err = identity.IntIdentifier(namespaceID, identifier)
if err != nil {
l.log.Warn("Error while parsing userID", "namespaceID", namespaceID, "userID", identifier)
}
var userID int64
if id, err := identity.UserIdentifier(signedInUser.GetID()); err == nil {
userID = id
}
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
@ -593,13 +589,8 @@ func (l *LibraryElementService) patchLibraryElement(c context.Context, signedInU
}
var userID int64
namespaceID, identifier := signedInUser.GetTypedID()
if namespaceID == identity.TypeUser || namespaceID == identity.TypeServiceAccount {
var errID error
userID, errID = identity.IntIdentifier(namespaceID, identifier)
if errID != nil {
l.log.Warn("Error while parsing userID", "namespaceID", namespaceID, "userID", identifier, "err", errID)
}
if id, err := identity.UserIdentifier(signedInUser.GetID()); err == nil {
userID = id
}
metrics.MFolderIDsServiceCount.WithLabelValues(metrics.LibraryElements).Inc()
@ -800,13 +791,9 @@ func (l *LibraryElementService) connectElementsToDashboardID(c context.Context,
return err
}
namespaceID, identifier := signedInUser.GetTypedID()
userID := int64(0)
if namespaceID == identity.TypeUser || namespaceID == identity.TypeServiceAccount {
userID, err = identity.IntIdentifier(namespaceID, identifier)
if err != nil {
l.log.Warn("Failed to parse user ID from namespace identifier", "namespace", namespaceID, "identifier", identifier, "error", err)
}
var userID int64
if id, err := identity.UserIdentifier(signedInUser.GetID()); err == nil {
userID = id
}
connection := model.LibraryElementConnection{

View File

@ -284,11 +284,9 @@ func ProvideService(plugCtxProvider *plugincontext.Provider, cfg *setting.Cfg, r
g.websocketHandler = func(ctx *contextmodel.ReqContext) {
user := ctx.SignedInUser
_, identifier := user.GetTypedID()
// Centrifuge expects Credentials in context with a current user ID.
cred := &centrifuge.Credentials{
UserID: identifier,
UserID: user.GetID().ID(),
}
newCtx := centrifuge.SetCredentials(ctx.Req.Context(), cred)
newCtx = livecontext.SetContextSignedUser(newCtx, user)
@ -955,8 +953,7 @@ func (g *GrafanaLive) HandleHTTPPublish(ctx *contextmodel.ReqContext) response.R
return response.Error(http.StatusBadRequest, "invalid channel ID", nil)
}
namespaceID, userID := ctx.SignedInUser.GetTypedID()
logger.Debug("Publish API cmd", "namespaceID", namespaceID, "userID", userID, "channel", cmd.Channel)
logger.Debug("Publish API cmd", "identity", ctx.SignedInUser.GetID(), "channel", cmd.Channel)
user := ctx.SignedInUser
channel := cmd.Channel
@ -1013,7 +1010,7 @@ func (g *GrafanaLive) HandleHTTPPublish(ctx *contextmodel.ReqContext) response.R
return response.Error(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError), nil)
}
}
logger.Debug("Publication successful", "namespaceID", namespaceID, "userID", userID, "channel", cmd.Channel)
logger.Debug("Publication successful", "identity", ctx.SignedInUser.GetID(), "channel", cmd.Channel)
return response.JSON(http.StatusOK, dtos.LivePublishResponse{})
}

View File

@ -73,7 +73,7 @@ func TestStreamManager_SubmitStream_Send(t *testing.T) {
}
mockContextGetter.EXPECT().GetPluginContext(context.Background(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, user identity.Requester, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error) {
userID, err := identity.IntIdentifier(user.GetTypedID())
userID, err := identity.IntIdentifier(user.GetID())
require.NoError(t, err)
require.Equal(t, int64(2), userID)
require.Equal(t, int64(1), user.GetOrgID())
@ -258,7 +258,7 @@ func TestStreamManager_SubmitStream_ErrorRestartsRunStream(t *testing.T) {
}
mockContextGetter.EXPECT().GetPluginContext(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, user identity.Requester, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error) {
userID, err := identity.IntIdentifier(user.GetTypedID())
userID, err := identity.IntIdentifier(user.GetID())
require.NoError(t, err)
require.Equal(t, int64(2), userID)
require.Equal(t, int64(1), user.GetOrgID())
@ -343,7 +343,7 @@ func TestStreamManager_HandleDatasourceUpdate(t *testing.T) {
}
mockContextGetter.EXPECT().GetPluginContext(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, user identity.Requester, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error) {
userID, err := identity.IntIdentifier(user.GetTypedID())
userID, err := identity.IntIdentifier(user.GetID())
require.NoError(t, err)
require.Equal(t, int64(2), userID)
@ -412,7 +412,7 @@ func TestStreamManager_HandleDatasourceDelete(t *testing.T) {
}
mockContextGetter.EXPECT().GetPluginContext(context.Background(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, user identity.Requester, pluginID string, datasourceUID string, skipCache bool) (backend.PluginContext, bool, error) {
userID, err := identity.IntIdentifier(user.GetTypedID())
userID, err := identity.IntIdentifier(user.GetID())
require.NoError(t, err)
require.Equal(t, int64(2), userID)
require.Equal(t, int64(1), user.GetOrgID())

View File

@ -297,7 +297,7 @@ func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLin
func (s *ServiceImpl) buildStarredItemsNavLinks(c *contextmodel.ReqContext) ([]*navtree.NavLink, error) {
starredItemsChildNavs := []*navtree.NavLink{}
userID, _ := identity.UserIdentifier(c.SignedInUser.GetTypedID())
userID, _ := identity.UserIdentifier(c.SignedInUser.GetID())
query := star.GetUserStarsQuery{
UserID: userID,
}

View File

@ -76,9 +76,9 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceU
return toNamespaceErrorResponse(err)
}
userNamespace, id := c.SignedInUser.GetTypedID()
userNamespace, id := c.SignedInUser.GetID().Type(), c.SignedInUser.GetID().ID()
var loggerCtx = []any{
"userId",
"identity",
id,
"userNamespace",
userNamespace,
@ -283,7 +283,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *contextmodel.ReqContext) response.Res
for groupKey, rules := range configs {
folder, ok := namespaceMap[groupKey.NamespaceUID]
if !ok {
userNamespace, id := c.SignedInUser.GetTypedID()
userNamespace, id := c.SignedInUser.GetID().Type(), c.SignedInUser.GetID().ID()
srv.log.Error("Namespace not visible to the user", "user", id, "userNamespace", userNamespace, "namespace", groupKey.NamespaceUID)
continue
}
@ -359,7 +359,7 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
var finalChanges *store.GroupDelta
var dbConfig *ngmodels.AlertConfiguration
err := srv.xactManager.InTransaction(c.Req.Context(), func(tranCtx context.Context) error {
userNamespace, id := c.SignedInUser.GetTypedID()
userNamespace, id := c.SignedInUser.GetID().Type(), c.SignedInUser.GetID().ID()
logger := srv.log.New("namespace_uid", groupKey.NamespaceUID, "group",
groupKey.RuleGroup, "org_id", groupKey.OrgID, "user_id", id, "userNamespace", userNamespace)
groupChanges, err := store.CalculateChanges(tranCtx, srv.store, groupKey, rules)
@ -454,7 +454,7 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
}
if len(finalChanges.New) > 0 {
userID, _ := identity.UserIdentifier(c.SignedInUser.GetTypedID())
userID, _ := identity.UserIdentifier(c.SignedInUser.GetID())
limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, ngmodels.QuotaTargetSrv, &quota.ScopeParameters{
OrgID: c.SignedInUser.GetOrgID(),
UserID: userID,

View File

@ -659,12 +659,10 @@ func (service *AlertRuleService) DeleteAlertRule(ctx context.Context, user ident
// checkLimitsTransactionCtx checks whether the current transaction (as identified by the ctx) breaches configured alert rule limits.
func (service *AlertRuleService) checkLimitsTransactionCtx(ctx context.Context, user identity.Requester) error {
// default to 0 if there is no user
userID := int64(0)
u, err := identity.UserIdentifier(user.GetTypedID())
if err != nil {
return fmt.Errorf("failed to check alert rule quota: %w", err)
var userID int64
if id, err := identity.UserIdentifier(user.GetID()); err == nil {
userID = id
}
userID = u
limitReached, err := service.quotas.CheckQuotaReached(ctx, models.QuotaTargetSrv, &quota.ScopeParameters{
OrgID: user.GetOrgID(),

View File

@ -94,17 +94,14 @@ func (o *Service) HasOAuthEntry(ctx context.Context, usr identity.Requester) (*l
return nil, false, nil
}
namespace, id := usr.GetTypedID()
if namespace != identity.TypeUser {
// Not a user, therefore no token.
if !identity.IsIdentityType(usr.GetID(), identity.TypeUser) {
return nil, false, nil
}
ctxLogger := logger.FromContext(ctx)
userID, err := identity.IntIdentifier(namespace, id)
userID, err := identity.UserIdentifier(usr.GetID())
if err != nil {
ctxLogger.Error("Failed to convert user id to int", "namespace", namespace, "userID", id, "error", err)
ctxLogger.Error("Failed to convert user id to int", "id", usr.GetID(), "error", err)
return nil, false, err
}
@ -130,24 +127,22 @@ func (o *Service) HasOAuthEntry(ctx context.Context, usr identity.Requester) (*l
// TryTokenRefresh returns an error in case the OAuth token refresh was unsuccessful
// It uses a singleflight.Group to prevent getting the Refresh Token multiple times for a given User
func (o *Service) TryTokenRefresh(ctx context.Context, usr identity.Requester) error {
ctxLogger := logger.FromContext(ctx)
if usr == nil || usr.IsNil() {
logger.Warn("Can only refresh OAuth tokens for existing users", "user", "nil")
ctxLogger.Warn("Can only refresh OAuth tokens for existing users", "user", "nil")
// Not user, no token.
return nil
}
namespace, id := usr.GetTypedID()
if namespace != identity.TypeUser {
// Not a user, therefore no token.
logger.Warn("Can only refresh OAuth tokens for users", "namespace", namespace, "userId", id)
if !identity.IsIdentityType(usr.GetID(), identity.TypeUser) {
ctxLogger.Warn("Can only refresh OAuth tokens for users", "id", usr.GetID())
return nil
}
ctxLogger := logger.FromContext(ctx)
userID, err := identity.IntIdentifier(namespace, id)
userID, err := identity.UserIdentifier(usr.GetID())
if err != nil {
ctxLogger.Warn("Failed to convert user id to int", "namespace", namespace, "userId", id, "error", err)
ctxLogger.Warn("Failed to convert user id to int", "id", usr.GetID(), "error", err)
return nil
}

View File

@ -35,8 +35,7 @@ func (m *UserHeaderMiddleware) applyUserHeader(ctx context.Context, h backend.Fo
}
h.DeleteHTTPHeader(proxyutil.UserHeaderName)
namespace, _ := reqCtx.SignedInUser.GetTypedID()
if namespace != identity.TypeAnonymous {
if !identity.IsIdentityType(reqCtx.SignedInUser.GetID(), identity.TypeAnonymous) {
h.SetHTTPHeader(proxyutil.UserHeaderName, reqCtx.SignedInUser.GetLogin())
}
}

View File

@ -99,8 +99,7 @@ func (api *ServiceAccountsAPI) CreateServiceAccount(c *contextmodel.ReqContext)
}
if api.cfg.RBAC.PermissionsOnCreation("service-account") {
t, _ := c.SignedInUser.GetTypedID()
if t == identity.TypeUser {
if identity.IsIdentityType(c.SignedInUser.GetID(), identity.TypeUser) {
userID, err := c.SignedInUser.GetID().ParseInt()
if err != nil {
return response.Error(http.StatusInternalServerError, "Failed to parse user id", err)

View File

@ -165,10 +165,9 @@ func (f *accessControlDashboardPermissionFilter) buildClauses() {
dashWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeDashboardsPrefix)
folderWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
userID := int64(0)
namespace, identifier := f.user.GetTypedID()
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
userID, _ = identity.IntIdentifier(namespace, identifier)
var userID int64
if id, err := identity.UserIdentifier(f.user.GetID()); err == nil {
userID = id
}
orgID := f.user.GetOrgID()

View File

@ -33,10 +33,9 @@ func (f *accessControlDashboardPermissionFilterNoFolderSubquery) buildClauses()
dashWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeDashboardsPrefix)
folderWildcards := accesscontrol.WildcardsFromPrefix(dashboards.ScopeFoldersPrefix)
userID := int64(0)
namespace, identifier := f.user.GetTypedID()
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
userID, _ = identity.IntIdentifier(namespace, identifier)
var userID int64
if id, err := identity.UserIdentifier(f.user.GetID()); err == nil {
userID = id
}
orgID := f.user.GetOrgID()

View File

@ -47,14 +47,9 @@ func (api *API) getDashboardHelper(ctx context.Context, orgID int64, id int64, u
}
func (api *API) GetStars(c *contextmodel.ReqContext) response.Response {
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser && namespace != identity.TypeServiceAccount {
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Invalid user ID", err)
return response.Error(http.StatusBadRequest, "Only users and service accounts get starred dashboards", nil)
}
query := star.GetUserStarsQuery{
@ -101,17 +96,12 @@ func (api *API) GetStars(c *contextmodel.ReqContext) response.Response {
// 403: forbiddenError
// 500: internalServerError
func (api *API) StarDashboard(c *contextmodel.ReqContext) response.Response {
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser && namespace != identity.TypeServiceAccount {
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "Invalid user ID", err)
}
idString := web.Params(c.Req)[":id"]
id, err := strconv.ParseInt(idString, 10, 64)
id, err := strconv.ParseInt(web.Params(c.Req)[":id"], 10, 64)
if err != nil {
return response.Error(http.StatusBadRequest, "Invalid dashboard ID", nil)
}
@ -146,18 +136,12 @@ func (api *API) StarDashboardByUID(c *contextmodel.ReqContext) response.Response
return response.Error(http.StatusBadRequest, "Invalid dashboard UID", nil)
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser && namespace != identity.TypeServiceAccount {
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
if err != nil {
return response.Error(http.StatusInternalServerError, "Invalid user ID", err)
}
dash, rsp := api.getDashboardHelper(c.Req.Context(), c.SignedInUser.GetOrgID(), 0, uid)
if rsp != nil {
return rsp
}
@ -193,14 +177,9 @@ func (api *API) UnstarDashboard(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusBadRequest, "Invalid dashboard ID", nil)
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser && namespace != identity.TypeServiceAccount {
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Invalid user ID", err)
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
cmd := star.UnstarDashboardCommand{UserID: userID, DashboardID: id}
@ -233,14 +212,9 @@ func (api *API) UnstarDashboardByUID(c *contextmodel.ReqContext) response.Respon
return response.Error(http.StatusBadRequest, "Invalid dashboard UID", nil)
}
namespace, identifier := c.SignedInUser.GetTypedID()
if namespace != identity.TypeUser && namespace != identity.TypeServiceAccount {
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
userID, err := identity.IntIdentifier(namespace, identifier)
userID, err := identity.UserIdentifier(c.SignedInUser.GetID())
if err != nil {
return response.Error(http.StatusInternalServerError, "Invalid user ID", err)
return response.Error(http.StatusBadRequest, "Only users and service accounts can star dashboards", nil)
}
dash, rsp := api.getDashboardHelper(c.Req.Context(), c.SignedInUser.GetOrgID(), 0, uid)

View File

@ -49,7 +49,7 @@ func (tapi *TeamAPI) createTeam(c *contextmodel.ReqContext) response.Response {
// if the request is authenticated using API tokens
// the SignedInUser is an empty struct therefore
// an additional check whether it is an actual user is required
namespace, identifier := c.SignedInUser.GetTypedID()
namespace, identifier := c.SignedInUser.GetID().Type(), c.SignedInUser.GetID().ID()
switch namespace {
case identity.TypeUser:
userID, err := strconv.ParseInt(identifier, 10, 64)

View File

@ -169,7 +169,7 @@ func (u *SignedInUser) GetAllowedKubernetesNamespace() string {
// GetCacheKey returns a unique key for the entity.
// Add an extra prefix to avoid collisions with other caches
func (u *SignedInUser) GetCacheKey() string {
namespace, id := u.GetTypedID()
typ, id := u.GetID().Type(), u.GetID().ID()
if !u.HasUniqueId() {
// Hack use the org role as id for identities that do not have a unique id
// e.g. anonymous and render key.
@ -181,7 +181,7 @@ func (u *SignedInUser) GetCacheKey() string {
id = string(orgRole)
}
return fmt.Sprintf("%d-%s-%s", u.GetOrgID(), namespace, id)
return fmt.Sprintf("%d-%s-%s", u.GetOrgID(), typ, id)
}
// GetIsGrafanaAdmin returns true if the user is a server admin
@ -245,13 +245,11 @@ func (u *SignedInUser) GetOrgRole() identity.RoleType {
// GetID returns namespaced id for the entity
func (u *SignedInUser) GetID() identity.TypedID {
ns, id := u.GetTypedID()
ns, id := u.getTypeAndID()
return identity.NewTypedIDString(ns, id)
}
// GetTypedID returns the namespace and ID of the active entity
// The namespace is one of the constants defined in pkg/apimachinery/identity
func (u *SignedInUser) GetTypedID() (identity.IdentityType, string) {
func (u *SignedInUser) getTypeAndID() (identity.IdentityType, string) {
switch {
case u.ApiKeyID != 0:
return identity.TypeAPIKey, strconv.FormatInt(u.ApiKeyID, 10)

View File

@ -114,8 +114,7 @@ func ApplyUserHeader(sendUserHeader bool, req *http.Request, user identity.Reque
return
}
namespace, _ := user.GetTypedID()
if namespace == identity.TypeUser || namespace == identity.TypeServiceAccount {
if identity.IsIdentityType(user.GetID(), identity.TypeUser) {
req.Header.Set(UserHeaderName, user.GetLogin())
}
}