github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/service/auth.go (about) 1 package service 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 8 "gorm.io/gorm" 9 10 "github.com/pyroscope-io/pyroscope/pkg/model" 11 ) 12 13 type AuthService struct { 14 db *gorm.DB 15 userService UserService 16 apiKeyService APIKeyService 17 jwtTokenService JWTTokenService 18 } 19 20 func NewAuthService(db *gorm.DB, jwtTokenService JWTTokenService, apiKeyService APIKeyService) AuthService { 21 return AuthService{ 22 db: db, 23 userService: NewUserService(db), 24 apiKeyService: apiKeyService, 25 jwtTokenService: jwtTokenService, 26 } 27 } 28 29 // AuthenticateUser returns User with the given login if its password hash 30 // matches the given one. If user cannot be found or the password does not 31 // match the function returns ErrCredentialsInvalid. 32 // 33 // External users are not allowed to use password authentication. 34 // TODO(kolesnikovae): It's true for "some" authentication providers. 35 // Others may need us to pass through the credentials (e.g. LDAP). 36 func (svc AuthService) AuthenticateUser(ctx context.Context, name string, password string) (model.User, error) { 37 user, err := svc.userService.FindUserByName(ctx, name) 38 switch { 39 case err == nil: 40 case errors.Is(err, model.ErrUserNotFound): 41 return model.User{}, model.ErrCredentialsInvalid 42 default: 43 return model.User{}, err 44 } 45 if model.IsUserExternal(user) { 46 // TODO(kolesnikovae): We could be a bit more specific here 47 // disclosing some info: e.g. tell which auth provider to use. 48 return model.User{}, model.ErrCredentialsInvalid 49 } 50 if model.IsUserDisabled(user) { 51 return model.User{}, model.ErrUserDisabled 52 } 53 if err = model.VerifyPassword(user.PasswordHash, password); err != nil { 54 return model.User{}, model.ErrCredentialsInvalid 55 } 56 return user, nil 57 } 58 59 func (svc AuthService) APIKeyFromToken(ctx context.Context, t string) (model.APIKey, error) { 60 id, secret, err := model.DecodeAPIKey(t) 61 if err != nil { 62 return model.APIKey{}, err 63 } 64 apiKey, err := svc.apiKeyService.FindAPIKeyByID(ctx, id) 65 if err != nil { 66 return model.APIKey{}, err 67 } 68 if err = apiKey.Verify(secret); err != nil { 69 return model.APIKey{}, err 70 } 71 return apiKey, nil 72 } 73 74 func (svc AuthService) UserFromJWTToken(ctx context.Context, t string) (model.User, error) { 75 token, err := svc.jwtTokenService.Parse(t) 76 if err != nil { 77 return model.User{}, fmt.Errorf("invalid jwt token: %w", err) 78 } 79 userToken, ok := svc.jwtTokenService.UserFromJWTToken(token) 80 if !ok { 81 return model.User{}, fmt.Errorf("user token is invalid") 82 } 83 user, err := svc.userService.FindUserByName(ctx, userToken.Name) 84 if err != nil { 85 return model.User{}, err 86 } 87 if model.IsUserDisabled(user) { 88 return model.User{}, model.ErrUserDisabled 89 } 90 if user.Role != userToken.Role { 91 return model.User{}, fmt.Errorf("user role has changed") 92 } 93 return user, nil 94 }