package auth import ( "context" "fmt" "git.perx.ru/perxis/perxis-go/pkg/collaborators" "git.perx.ru/perxis/perxis-go/pkg/environments" "git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/members" "git.perx.ru/perxis/perxis-go/pkg/permission" "git.perx.ru/perxis/perxis-go/pkg/roles" "git.perx.ru/perxis/perxis-go/pkg/spaces" "git.perx.ru/perxis/perxis-go/pkg/users" "git.perx.ru/perxis/perxis/services" ) type UserPrincipal struct { id string identity string user *users.User invalid bool spaceID string orgID string users users.Users members members.Members hasMemberRole bool memberRole members.Role collaborators collaborators.Collaborators spaces spaces.Spaces environments environments.Environments roles roles.Roles } func (u UserPrincipal) Format(f fmt.State, verb rune) { f.Write([]byte(fmt.Sprintf("UserPrincipal{ID: '%s', Identity: '%s'}", u.id, u.identity))) } func (u *UserPrincipal) Space(spaceID string) SpaceAccessor { u.spaceID = spaceID u.orgID = "" return u } func (u *UserPrincipal) getSpace(ctx context.Context, spaceID string) *spaces.Space { if spaceID == "" { return nil } space, _ := u.spaces.Get(WithSystem(ctx), spaceID) return space } func (u UserPrincipal) Organization(orgID string) OrganizationAccessor { u.orgID = orgID return &u } func (u *UserPrincipal) GetID(ctx context.Context) string { user := u.User(ctx) if user == nil { return "" } return user.ID } func (u *UserPrincipal) GetIdentity(ctx context.Context) string { return u.identity } func (u *UserPrincipal) IsValid(ctx context.Context) bool { if u == nil { return false } return u.User(ctx) != nil } func (u *UserPrincipal) IsSystem(ctx context.Context) bool { user := u.User(ctx) if user != nil { return user.IsSystem() } return false } func (u *UserPrincipal) IsManagementAllowed(ctx context.Context, spaceID string) error { if !u.IsValid(ctx) { return ErrAccessDenied } if u.IsSystem(ctx) { return nil } if u.Member(ctx).IsPrivileged() { return nil } if role := u.Role(ctx, spaceID); role != nil && role.AllowManagement { return nil } return ErrAccessDenied } func (u *UserPrincipal) User(ctx context.Context) *users.User { if u.invalid { return nil } if u.user != nil { return u.user } if u.users == nil { u.invalid = true return nil } var user *users.User var err error switch { case u.id != "": user, err = u.users.Get(WithSystem(ctx), u.id) case u.identity != "": ctx = WithSystem(ctx) user, err = u.users.GetByIdentity(WithSystem(ctx), u.identity) } if err != nil || user == nil { u.invalid = true return nil } u.user = user return u.user } func (u *UserPrincipal) Member(ctx context.Context) members.Role { if u.hasMemberRole { return u.memberRole } if u.members == nil || (u.orgID == "" && u.spaceID == "") { u.hasMemberRole = true return members.NotMember } if u.orgID == "" && u.spaceID != "" { sp := u.getSpace(ctx, u.spaceID) if sp == nil { u.hasMemberRole = true return members.NotMember } u.orgID = sp.OrgID } role, err := u.members.Get(WithSystem(ctx), u.orgID, u.GetID(ctx)) if err != nil { role = members.NotMember } u.memberRole = role u.hasMemberRole = true return u.memberRole } // HasSpaceAccess проверяет, есть ли у пользователя доступ к пространству // Пользователь имеет доступ к пространству если: // - Является участником пространства (даже если его роль не существует) // - Пространство позволяет доступ для не участников (есть роли AnonymousRole/AuthorizedRole/ViewRole) // Deprecated :use HasAccess func (u *UserPrincipal) HasSpaceAccess(ctx context.Context, spaceID string) bool { res, _ := u.hasRole(ctx, spaceID) return res } // HasAccess проверяет, есть ли у пользователя доступ к пространству // Пользователь имеет доступ к пространству если: // - Является участником пространства (даже если его роль не существует) // - Пространство позволяет доступ для не участников (есть роли AnonymousRole/AuthorizedRole/ViewRole) func (u *UserPrincipal) HasAccess(ctx context.Context, spaceID, orgID string) error { if !u.IsValid(ctx) { return services.ErrAccessDenied } if u.IsSystem(ctx) { return nil } if spaceID != "" { hasAllow, err := u.hasRole(ctx, spaceID) if err != nil { return err } if hasAllow { return nil } } if orgID != "" { if u.Organization(orgID).Member(ctx).IsPrivileged() { return nil } } else { if u.Member(ctx).IsPrivileged() { return nil } } return services.ErrAccessDenied } func (u *UserPrincipal) hasRole(ctx context.Context, spaceID string) (bool, error) { if u.spaceID == "" || spaceID == "" { return false, nil } ctx = WithSystem(ctx) if spaceID != u.spaceID { _, cErr := u.collaborators.Get(ctx, spaceID, u.spaceID) if cErr == nil { return true, nil } _, rErr := u.roles.Get(ctx, spaceID, roles.ViewRole) if rErr == nil { return true, nil } if errors.Is(cErr, services.ErrNotFound) || errors.Is(rErr, services.ErrNotFound) { if sp := u.getSpace(ctx, spaceID); sp == nil { return false, services.ErrNotFound } } return false, nil } _, cErr := u.collaborators.Get(ctx, spaceID, u.GetID(ctx)) if cErr == nil { return true, nil } _, rErr := u.roles.Get(ctx, spaceID, roles.AuthorizedRole) if rErr == nil { return true, nil } if errors.Is(cErr, services.ErrNotFound) || errors.Is(rErr, services.ErrNotFound) { if sp := u.getSpace(ctx, spaceID); sp == nil { return false, services.ErrNotFound } } return false, nil } func (u *UserPrincipal) getRoleID(ctx context.Context, spaceID string) string { if u.spaceID == "" || spaceID == "" { return "" } ctx = WithSystem(ctx) if spaceID != u.spaceID { rID, err := u.collaborators.Get(ctx, spaceID, u.spaceID) if err != nil { rID = roles.ViewRole } return rID } if roleID, err := u.collaborators.Get(ctx, spaceID, u.GetID(ctx)); err == nil { return roleID } return roles.AuthorizedRole } func (u *UserPrincipal) Role(ctx context.Context, spaceID string) *roles.Role { if roleID := u.getRoleID(ctx, spaceID); roleID != "" { role, _ := u.roles.Get(WithSystem(ctx), spaceID, roleID) return role } return nil } func (u *UserPrincipal) Rules(ctx context.Context, spaceID, envID string) permission.Ruleset { if spaceID == "" || envID == "" { return nil } if u.spaceID == spaceID && (u.IsSystem(ctx) || u.Member(ctx).IsPrivileged()) { return permission.PrivilegedRuleset{} } role := u.Role(ctx, spaceID) if role == nil { return nil } if !hasEnvironmentAccess(ctx, u.environments, role, envID) { return nil } return role.Rules } func IsValidUser(ctx context.Context, p Principal) bool { if p == nil { return false } if u, ok := p.(*UserPrincipal); ok { return u.IsValid(ctx) } return false } func User(ctx context.Context, p Principal) *users.User { if u, ok := p.(*UserPrincipal); ok { return u.User(ctx) } return nil } func (u *UserPrincipal) HasEnvironmentAccess(ctx context.Context, spaceID, env string) bool { return hasEnvironmentAccess(ctx, u.environments, u.Role(ctx, spaceID), env) }