Refactor auth package (#17962)

This commit is contained in:
Lunny Xiao 2022-01-02 21:12:35 +08:00 committed by GitHub
parent e61b390d54
commit de8e3948a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
87 changed files with 2880 additions and 2770 deletions

View file

@ -11,8 +11,8 @@ import (
"strings"
"time"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@ -92,7 +92,7 @@ func addKey(e db.Engine, key *PublicKey) (err error) {
}
// AddPublicKey adds new public key to database and authorized_keys file.
func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*PublicKey, error) {
func AddPublicKey(ownerID int64, name, content string, authSourceID int64) (*PublicKey, error) {
log.Trace(content)
fingerprint, err := calcFingerprint(content)
@ -128,7 +128,7 @@ func AddPublicKey(ownerID int64, name, content string, loginSourceID int64) (*Pu
Content: content,
Mode: perm.AccessModeWrite,
Type: KeyTypeUser,
LoginSourceID: loginSourceID,
LoginSourceID: authSourceID,
}
if err = addKey(sess, key); err != nil {
return nil, fmt.Errorf("addKey: %v", err)
@ -223,10 +223,10 @@ func CountPublicKeys(userID int64) (int64, error) {
}
// ListPublicKeysBySource returns a list of synchronized public keys for a given user and login source.
func ListPublicKeysBySource(uid, loginSourceID int64) ([]*PublicKey, error) {
func ListPublicKeysBySource(uid, authSourceID int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
return keys, db.GetEngine(db.DefaultContext).
Where("owner_id = ? AND login_source_id = ?", uid, loginSourceID).
Where("owner_id = ? AND login_source_id = ?", uid, authSourceID).
Find(&keys)
}
@ -261,7 +261,7 @@ func DeletePublicKeys(ctx context.Context, keyIDs ...int64) error {
// PublicKeysAreExternallyManaged returns whether the provided KeyID represents an externally managed Key
func PublicKeysAreExternallyManaged(keys []*PublicKey) ([]bool, error) {
sources := make([]*login.Source, 0, 5)
sources := make([]*auth.Source, 0, 5)
externals := make([]bool, len(keys))
keyloop:
for i, key := range keys {
@ -270,7 +270,7 @@ keyloop:
continue keyloop
}
var source *login.Source
var source *auth.Source
sourceloop:
for _, s := range sources {
@ -282,11 +282,11 @@ keyloop:
if source == nil {
var err error
source, err = login.GetSourceByID(key.LoginSourceID)
source, err = auth.GetSourceByID(key.LoginSourceID)
if err != nil {
if login.IsErrSourceNotExist(err) {
if auth.IsErrSourceNotExist(err) {
externals[i] = false
sources[i] = &login.Source{
sources[i] = &auth.Source{
ID: key.LoginSourceID,
}
continue keyloop
@ -295,7 +295,7 @@ keyloop:
}
}
if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
// Disable setting SSH keys for this user
externals[i] = true
}
@ -313,14 +313,14 @@ func PublicKeyIsExternallyManaged(id int64) (bool, error) {
if key.LoginSourceID == 0 {
return false, nil
}
source, err := login.GetSourceByID(key.LoginSourceID)
source, err := auth.GetSourceByID(key.LoginSourceID)
if err != nil {
if login.IsErrSourceNotExist(err) {
if auth.IsErrSourceNotExist(err) {
return false, nil
}
return false, err
}
if sshKeyProvider, ok := source.Cfg.(login.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
if sshKeyProvider, ok := source.Cfg.(auth.SSHKeyProvider); ok && sshKeyProvider.ProvidesSSHKeys() {
// Disable setting SSH keys for this user
return true, nil
}
@ -360,7 +360,7 @@ func deleteKeysMarkedForDeletion(keys []string) (bool, error) {
}
// AddPublicKeysBySource add a users public keys. Returns true if there are changes.
func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
func AddPublicKeysBySource(usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
var sshKeysNeedUpdate bool
for _, sshKey := range sshPublicKeys {
var err error
@ -398,7 +398,7 @@ func AddPublicKeysBySource(usr *user_model.User, s *login.Source, sshPublicKeys
}
// SynchronizePublicKeys updates a users public keys. Returns true if there are changes.
func SynchronizePublicKeys(usr *user_model.User, s *login.Source, sshPublicKeys []string) bool {
func SynchronizePublicKeys(usr *user_model.User, s *auth.Source, sshPublicKeys []string) bool {
var sshKeysNeedUpdate bool
log.Trace("synchronizePublicKeys[%s]: Handling Public SSH Key synchronization for user %s", s.Name, usr.Name)

View file

@ -25,7 +25,7 @@ import (
// This file contains functions related to principals
// AddPrincipalKey adds new principal to database and authorized_principals file.
func AddPrincipalKey(ownerID int64, content string, loginSourceID int64) (*PublicKey, error) {
func AddPrincipalKey(ownerID int64, content string, authSourceID int64) (*PublicKey, error) {
ctx, committer, err := db.TxContext()
if err != nil {
return nil, err
@ -49,7 +49,7 @@ func AddPrincipalKey(ownerID int64, content string, loginSourceID int64) (*Publi
Content: content,
Mode: perm.AccessModeWrite,
Type: KeyTypePrincipal,
LoginSourceID: loginSourceID,
LoginSourceID: authSourceID,
}
if err = addPrincipalKey(sess, key); err != nil {
return nil, fmt.Errorf("addKey: %v", err)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"path/filepath"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"crypto/sha256"
@ -510,3 +510,55 @@ func revokeOAuth2Grant(e db.Engine, grantID, userID int64) error {
_, err := e.Delete(&OAuth2Grant{ID: grantID, UserID: userID})
return err
}
// ErrOAuthClientIDInvalid will be thrown if client id cannot be found
type ErrOAuthClientIDInvalid struct {
ClientID string
}
// IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist.
func IsErrOauthClientIDInvalid(err error) bool {
_, ok := err.(ErrOAuthClientIDInvalid)
return ok
}
// Error returns the error message
func (err ErrOAuthClientIDInvalid) Error() string {
return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID)
}
// ErrOAuthApplicationNotFound will be thrown if id cannot be found
type ErrOAuthApplicationNotFound struct {
ID int64
}
// IsErrOAuthApplicationNotFound checks if an error is a ErrReviewNotExist.
func IsErrOAuthApplicationNotFound(err error) bool {
_, ok := err.(ErrOAuthApplicationNotFound)
return ok
}
// Error returns the error message
func (err ErrOAuthApplicationNotFound) Error() string {
return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID)
}
// GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources
func GetActiveOAuth2ProviderSources() ([]*Source, error) {
sources := make([]*Source, 0, 1)
if err := db.GetEngine(db.DefaultContext).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil {
return nil, err
}
return sources, nil
}
// GetActiveOAuth2SourceByName returns a OAuth2 AuthSource based on the given name
func GetActiveOAuth2SourceByName(name string) (*Source, error) {
authSource := new(Source)
has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource)
if !has || err != nil {
return nil, err
}
return authSource, nil
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"testing"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"fmt"

View file

@ -3,12 +3,11 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"fmt"
"reflect"
"strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@ -84,10 +83,7 @@ type RegisterableSource interface {
UnregisterSource() error
}
// SourceSettable configurations can have their loginSource set on them
type SourceSettable interface {
SetLoginSource(*Source)
}
var registeredConfigs = map[Type]func() Config{}
// RegisterTypeConfig register a config for a provided type
func RegisterTypeConfig(typ Type, exemplar Config) {
@ -105,7 +101,10 @@ func RegisterTypeConfig(typ Type, exemplar Config) {
}
}
var registeredConfigs = map[Type]func() Config{}
// SourceSettable configurations can have their authSource set on them
type SourceSettable interface {
SetAuthSource(*Source)
}
// Source represents an external way for authorizing users.
type Source struct {
@ -129,30 +128,17 @@ func init() {
db.RegisterModel(new(Source))
}
// Cell2Int64 converts a xorm.Cell type to int64,
// and handles possible irregular cases.
func Cell2Int64(val xorm.Cell) int64 {
switch (*val).(type) {
case []uint8:
log.Trace("Cell2Int64 ([]uint8): %v", *val)
v, _ := strconv.ParseInt(string((*val).([]uint8)), 10, 64)
return v
}
return (*val).(int64)
}
// BeforeSet is invoked from XORM before setting the value of a field of this object.
func (source *Source) BeforeSet(colName string, val xorm.Cell) {
if colName == "type" {
typ := Type(Cell2Int64(val))
typ := Type(db.Cell2Int64(val))
constructor, ok := registeredConfigs[typ]
if !ok {
return
}
source.Cfg = constructor()
if settable, ok := source.Cfg.(SourceSettable); ok {
settable.SetLoginSource(source)
settable.SetAuthSource(source)
}
}
}
@ -211,7 +197,7 @@ func (source *Source) SkipVerify() bool {
return ok && skipVerifiable.IsSkipVerify()
}
// CreateSource inserts a LoginSource in the DB if not already
// CreateSource inserts a AuthSource in the DB if not already
// existing with the given name.
func CreateSource(source *Source) error {
has, err := db.GetEngine(db.DefaultContext).Where("name=?", source.Name).Exist(new(Source))
@ -235,7 +221,7 @@ func CreateSource(source *Source) error {
}
if settable, ok := source.Cfg.(SourceSettable); ok {
settable.SetLoginSource(source)
settable.SetAuthSource(source)
}
registerableSource, ok := source.Cfg.(RegisterableSource)
@ -245,7 +231,7 @@ func CreateSource(source *Source) error {
err = registerableSource.RegisterSource()
if err != nil {
// remove the LoginSource in case of errors while registering configuration
// remove the AuthSource in case of errors while registering configuration
if _, err := db.GetEngine(db.DefaultContext).Delete(source); err != nil {
log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err)
}
@ -322,11 +308,11 @@ func GetSourceByID(id int64) (*Source, error) {
// UpdateSource updates a Source record in DB.
func UpdateSource(source *Source) error {
var originalLoginSource *Source
var originalSource *Source
if source.IsOAuth2() {
// keep track of the original values so we can restore in case of errors while registering OAuth2 providers
var err error
if originalLoginSource, err = GetSourceByID(source.ID); err != nil {
if originalSource, err = GetSourceByID(source.ID); err != nil {
return err
}
}
@ -341,7 +327,7 @@ func UpdateSource(source *Source) error {
}
if settable, ok := source.Cfg.(SourceSettable); ok {
settable.SetLoginSource(source)
settable.SetAuthSource(source)
}
registerableSource, ok := source.Cfg.(RegisterableSource)
@ -352,7 +338,7 @@ func UpdateSource(source *Source) error {
err = registerableSource.RegisterSource()
if err != nil {
// restore original values since we cannot update the provider it self
if _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(originalLoginSource); err != nil {
if _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(originalSource); err != nil {
log.Error("UpdateSource: Error while wrapOpenIDConnectInitializeError: %v", err)
}
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"strings"
@ -34,10 +34,10 @@ func (source *TestSource) ToDB() ([]byte, error) {
return json.Marshal(source)
}
func TestDumpLoginSource(t *testing.T) {
func TestDumpAuthSource(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
loginSourceSchema, err := db.TableInfo(new(Source))
authSourceSchema, err := db.TableInfo(new(Source))
assert.NoError(t, err)
RegisterTypeConfig(OAuth2, new(TestSource))
@ -54,7 +54,7 @@ func TestDumpLoginSource(t *testing.T) {
sb := new(strings.Builder)
db.DumpTables([]*schemas.Table{loginSourceSchema}, sb)
db.DumpTables([]*schemas.Table{authSourceSchema}, sb)
assert.Contains(t, sb.String(), `"Provider":"ConvertibleSourceName"`)
}

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"crypto/md5"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"fmt"

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
package auth
import (
"encoding/hex"

View file

@ -6,9 +6,12 @@ package db
import (
"fmt"
"strconv"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
@ -39,3 +42,16 @@ func ConvertUtf8ToUtf8mb4() error {
return nil
}
// Cell2Int64 converts a xorm.Cell type to int64,
// and handles possible irregular cases.
func Cell2Int64(val xorm.Cell) int64 {
switch (*val).(type) {
case []uint8:
log.Trace("Cell2Int64 ([]uint8): %v", *val)
v, _ := strconv.ParseInt(string((*val).([]uint8)), 10, 64)
return v
}
return (*val).(int64)
}

View file

@ -1,70 +0,0 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package login
import (
"fmt"
"code.gitea.io/gitea/models/db"
)
// ________ _____ __ .__
// \_____ \ / _ \ __ ___/ |_| |__
// / | \ / /_\ \| | \ __\ | \
// / | \/ | \ | /| | | Y \
// \_______ /\____|__ /____/ |__| |___| /
// \/ \/ \/
// ErrOAuthClientIDInvalid will be thrown if client id cannot be found
type ErrOAuthClientIDInvalid struct {
ClientID string
}
// IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist.
func IsErrOauthClientIDInvalid(err error) bool {
_, ok := err.(ErrOAuthClientIDInvalid)
return ok
}
// Error returns the error message
func (err ErrOAuthClientIDInvalid) Error() string {
return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID)
}
// ErrOAuthApplicationNotFound will be thrown if id cannot be found
type ErrOAuthApplicationNotFound struct {
ID int64
}
// IsErrOAuthApplicationNotFound checks if an error is a ErrReviewNotExist.
func IsErrOAuthApplicationNotFound(err error) bool {
_, ok := err.(ErrOAuthApplicationNotFound)
return ok
}
// Error returns the error message
func (err ErrOAuthApplicationNotFound) Error() string {
return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID)
}
// GetActiveOAuth2ProviderLoginSources returns all actived LoginOAuth2 sources
func GetActiveOAuth2ProviderLoginSources() ([]*Source, error) {
sources := make([]*Source, 0, 1)
if err := db.GetEngine(db.DefaultContext).Where("is_active = ? and type = ?", true, OAuth2).Find(&sources); err != nil {
return nil, err
}
return sources, nil
}
// GetActiveOAuth2LoginSourceByName returns a OAuth2 LoginSource based on the given name
func GetActiveOAuth2LoginSourceByName(name string) (*Source, error) {
loginSource := new(Source)
has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(loginSource)
if !has || err != nil {
return nil, err
}
return loginSource, nil
}

View file

@ -8,7 +8,6 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/timeutil"
@ -170,7 +169,7 @@ func (cfg *PullRequestsConfig) AllowedMergeStyleCount() int {
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) {
switch colName {
case "type":
switch unit.Type(login.Cell2Int64(val)) {
switch unit.Type(db.Cell2Int64(val)) {
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
r.Config = new(UnitConfig)
case unit.TypeExternalWiki:

View file

@ -6,8 +6,8 @@ package models
import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
@ -21,7 +21,7 @@ type Statistic struct {
Repo, Watch, Star, Action, Access,
Issue, IssueClosed, IssueOpen,
Comment, Oauth, Follow,
Mirror, Release, LoginSource, Webhook,
Mirror, Release, AuthSource, Webhook,
Milestone, Label, HookTask,
Team, UpdateTask, Project,
ProjectBoard, Attachment int64
@ -98,7 +98,7 @@ func GetStatistic() (stats Statistic) {
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
stats.Counter.Release, _ = e.Count(new(Release))
stats.Counter.LoginSource = login.CountSources()
stats.Counter.AuthSource = auth.CountSources()
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(Milestone))
stats.Counter.Label, _ = e.Count(new(Label))

View file

@ -10,8 +10,8 @@ import (
"fmt"
"time"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
@ -68,7 +68,7 @@ func NewAccessToken(t *AccessToken) error {
}
t.TokenSalt = salt
t.Token = base.EncodeSha1(gouuid.New().String())
t.TokenHash = login.HashToken(t.Token, t.TokenSalt)
t.TokenHash = auth.HashToken(t.Token, t.TokenSalt)
t.TokenLastEight = t.Token[len(t.Token)-8:]
_, err = db.GetEngine(db.DefaultContext).Insert(t)
return err
@ -130,7 +130,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
}
for _, t := range tokens {
tempHash := login.HashToken(token, t.TokenSalt)
tempHash := auth.HashToken(token, t.TokenSalt)
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
if successfulAccessTokenCache != nil {
successfulAccessTokenCache.Add(token, t.ID)

View file

@ -7,8 +7,8 @@ package user
import (
"fmt"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
)
// UserList is a list of user.
@ -40,13 +40,13 @@ func (users UserList) GetTwoFaStatus() map[int64]bool {
return results
}
func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*login.TwoFactor, error) {
func (users UserList) loadTwoFactorStatus(e db.Engine) (map[int64]*auth.TwoFactor, error) {
if len(users) == 0 {
return nil, nil
}
userIDs := users.GetUserIDs()
tokenMaps := make(map[int64]*login.TwoFactor, len(userIDs))
tokenMaps := make(map[int64]*auth.TwoFactor, len(userIDs))
err := e.
In("uid", userIDs).
Find(&tokenMaps)

View file

@ -19,8 +19,8 @@ import (
_ "image/jpeg" // Needed for jpeg support
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/modules/auth/openid"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
@ -89,7 +89,7 @@ type User struct {
// is to change his/her password after registration.
MustChangePassword bool `xorm:"NOT NULL DEFAULT false"`
LoginType login.Type
LoginType auth.Type
LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
LoginName string
Type UserType
@ -232,12 +232,12 @@ func GetAllUsers() ([]*User, error) {
// IsLocal returns true if user login type is LoginPlain.
func (u *User) IsLocal() bool {
return u.LoginType <= login.Plain
return u.LoginType <= auth.Plain
}
// IsOAuth2 returns true if user login type is LoginOAuth2.
func (u *User) IsOAuth2() bool {
return u.LoginType == login.OAuth2
return u.LoginType == auth.OAuth2
}
// MaxCreationLimit returns the number of repositories a user is allowed to create
@ -1012,7 +1012,7 @@ func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error)
}
// GetUsersBySource returns a list of Users for a login source
func GetUsersBySource(s *login.Source) ([]*User, error) {
func GetUsersBySource(s *auth.Source) ([]*User, error) {
var users []*User
err := db.GetEngine(db.DefaultContext).Where("login_type = ? AND login_source = ?", s.Type, s.ID).Find(&users)
return users, err

View file

@ -9,8 +9,8 @@ import (
"strings"
"testing"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
@ -21,7 +21,7 @@ import (
func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &login.OAuth2Application{ID: 1}).(*login.OAuth2Application)
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1}).(*auth.OAuth2Application)
user, err := GetUserByID(app.UID)
assert.NoError(t, err)
assert.NotNil(t, user)