garm/auth/auth.go

137 lines
3.3 KiB
Go
Raw Normal View History

2022-04-28 16:13:20 +00:00
package auth
import (
"context"
"garm/config"
"garm/database/common"
runnerErrors "garm/errors"
"garm/params"
"garm/util"
2022-04-28 16:13:20 +00:00
"time"
"github.com/golang-jwt/jwt"
"github.com/nbutton23/zxcvbn-go"
"github.com/pkg/errors"
"golang.org/x/crypto/bcrypt"
)
func NewAuthenticator(cfg config.JWTAuth, store common.Store) *Authenticator {
return &Authenticator{
cfg: cfg,
store: store,
}
}
type Authenticator struct {
store common.Store
cfg config.JWTAuth
}
func (a *Authenticator) IsInitialized() bool {
2022-05-02 17:55:29 +00:00
if a.store.HasAdminUser(context.Background()) {
return true
2022-04-28 16:13:20 +00:00
}
2022-05-02 17:55:29 +00:00
return false
2022-04-28 16:13:20 +00:00
}
func (a *Authenticator) GetJWTToken(ctx context.Context) (string, error) {
tokenID, err := util.GetRandomString(16)
if err != nil {
return "", errors.Wrap(err, "generating random string")
}
expireToken := time.Now().Add(a.cfg.TimeToLive.Duration()).Unix()
claims := JWTClaims{
StandardClaims: jwt.StandardClaims{
ExpiresAt: expireToken,
// TODO: make this configurable
Issuer: "garm",
2022-04-28 16:13:20 +00:00
},
UserID: UserID(ctx),
TokenID: tokenID,
IsAdmin: IsAdmin(ctx),
FullName: FullName(ctx),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(a.cfg.Secret))
if err != nil {
return "", errors.Wrap(err, "fetching token string")
}
return tokenString, nil
}
func (a *Authenticator) InitController(ctx context.Context, param params.NewUserParams) (params.User, error) {
_, err := a.store.ControllerInfo()
if err != nil {
if !errors.Is(err, runnerErrors.ErrNotFound) {
return params.User{}, errors.Wrap(err, "initializing controller")
}
}
if a.store.HasAdminUser(ctx) {
return params.User{}, runnerErrors.ErrNotFound
}
if param.Email == "" || param.Username == "" {
return params.User{}, runnerErrors.NewBadRequestError("missing username or email")
}
if !util.IsValidEmail(param.Email) {
return params.User{}, runnerErrors.NewBadRequestError("invalid email address")
}
// username is varchar(64)
if len(param.Username) > 64 || !util.IsAlphanumeric(param.Username) {
return params.User{}, runnerErrors.NewBadRequestError("invalid username")
}
param.IsAdmin = true
param.Enabled = true
passwordStenght := zxcvbn.PasswordStrength(param.Password, nil)
if passwordStenght.Score < 4 {
return params.User{}, runnerErrors.NewBadRequestError("password is too weak")
}
hashed, err := util.PaswsordToBcrypt(param.Password)
if err != nil {
return params.User{}, errors.Wrap(err, "creating user")
}
param.Password = hashed
return a.store.CreateUser(ctx, param)
}
func (a *Authenticator) AuthenticateUser(ctx context.Context, info params.PasswordLoginParams) (context.Context, error) {
if info.Username == "" {
return ctx, runnerErrors.ErrUnauthorized
}
if info.Password == "" {
return ctx, runnerErrors.ErrUnauthorized
}
user, err := a.store.GetUser(ctx, info.Username)
if err != nil {
2022-05-03 12:40:59 +00:00
if errors.Is(err, runnerErrors.ErrNotFound) {
2022-04-28 16:13:20 +00:00
return ctx, runnerErrors.ErrUnauthorized
}
return ctx, errors.Wrap(err, "authenticating")
}
if !user.Enabled {
return ctx, runnerErrors.ErrUnauthorized
}
if user.Password == "" {
return ctx, runnerErrors.ErrUnauthorized
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(info.Password)); err != nil {
return ctx, runnerErrors.ErrUnauthorized
}
return PopulateContext(ctx, user), nil
}