Skip to content

Commit

Permalink
feat(api): This commit is to implement
Browse files Browse the repository at this point in the history
store functions for the recovery codes method
  • Loading branch information
nia committed Oct 10, 2023
1 parent 3800085 commit c1c3fd4
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 128 deletions.
2 changes: 0 additions & 2 deletions api/pkg/echo/handlers/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ func NewErrors(reporter *sentry.Client) func(error, echo.Context) {
}

var status int

switch e.Layer {
case guard.ErrLayer:
status = http.StatusForbidden
Expand All @@ -78,7 +77,6 @@ func NewErrors(reporter *sentry.Client) func(error, echo.Context) {
// service error affecting it, which requires fixing.
status = http.StatusInternalServerError
}

ctx.NoContent(status) //nolint:errcheck
}
}
51 changes: 28 additions & 23 deletions api/routes/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package routes

import (
"errors"
"fmt"
"net/http"
"strconv"

Expand Down Expand Up @@ -42,6 +41,7 @@ const (
// authentication. It gets the JWT token sent, unwraps it and sets the information, like tenant, user, etc., as headers
// of the response to be got in the subsequent through the [gateway.Context].
func (h *Handler) AuthRequest(c gateway.Context) error {

Check failure on line 43 in api/routes/auth.go

View workflow job for this annotation

GitHub Actions / validate (api)

unnecessary leading newline (whitespace)

token, ok := c.Get(middleware.DefaultJWTConfig.ContextKey).(*jwt.Token)
if !ok {
return svc.ErrTypeAssertion
Expand Down Expand Up @@ -93,22 +93,16 @@ func (h *Handler) AuthRequest(c gateway.Context) error {
}
}

MFA, err := h.service.AuthMFA(c.Ctx(), claims.ID)
if err != nil {
return err
}

if MFA != claims.MFA.Status {
if MFA {
if !claims.MFA.Validate {
return svc.NewErrAuthUnathorized(errors.New("necessary make validate MFA"))
}
}
}
// MFA, err := h.service.AuthMFA(c.Ctx(), claims.ID)
// if err != nil {
// return err
// }

fmt.Println("AUTH REQUEST SET HEADER MFA:", claims.MFA.Status)
fmt.Println("AUTH REQUEST SET HEADER validate:", claims.MFA.Validate)
fmt.Println("AUTH REQUEST SET HEADERs:", c.Response().Header())
// if MFA {
// if !claims.MFA.Validate {
// return svc.NewErrAuthUnathorized(errors.New("necessary make validate MFA"))
// }
// }

// Extract datas of user from JWT
c.Response().Header().Set("X-Tenant-ID", claims.Tenant)
Expand All @@ -131,6 +125,7 @@ func (h *Handler) AuthRequest(c gateway.Context) error {

return c.NoContent(http.StatusOK)
default:

return svc.NewErrAuthUnathorized(nil)
}
}
Expand Down Expand Up @@ -169,7 +164,10 @@ func (h *Handler) AuthUser(c gateway.Context) error {
return err
}

res, err := h.service.AuthUser(c.Ctx(), req)
validate := c.Request().Header.Get("X-Validate-MFA")
mfa, _ := strconv.ParseBool(validate)

res, err := h.service.AuthUser(c.Ctx(), req, mfa)

if err != nil {
if errors.Is(err, svc.ErrUserNotFound) {
Expand Down Expand Up @@ -206,7 +204,7 @@ func (h *Handler) AuthGetToken(c gateway.Context) error {
return err
}

res, err := h.service.AuthGetToken(c.Ctx(), req.Tenant)
res, err := h.service.AuthGetToken(c.Ctx(), req.Tenant, req.MFA)
if err != nil {
return err
}
Expand Down Expand Up @@ -260,7 +258,6 @@ func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
if !ok {
return svc.ErrTypeAssertion
}

jwt := middleware.JWTWithConfig(middleware.JWTConfig{ //nolint:staticcheck
Claims: &jwt.MapClaims{},
SigningKey: ctx.Service().(svc.Service).PublicKey(),
Expand All @@ -273,12 +270,20 @@ func AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc {

func AuthMiddlewareMFA(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Verify is the user have a 2fa enable

statusMFA := c.Request().Header.Get("X-MFA")
validateMFA := c.Request().Header.Get("X-Validate-MFA")

if statusMFA != "" {
exemptedPaths := []string{"/api/mfa/generate", "/api/mfa/enable", "/api/mfa/desable", "/api/mfa/recovery", "/api/mfa/auth"}
currentPath := c.Path()
isExempted := false

for _, path := range exemptedPaths {
if currentPath == path {
isExempted = true
}
}

if !isExempted && statusMFA != "" {
status, err := strconv.ParseBool(statusMFA)
if err != nil {
return err
Expand All @@ -291,7 +296,7 @@ func AuthMiddlewareMFA(next echo.HandlerFunc) echo.HandlerFunc {
}

if !validate {
return svc.NewErrMFAUnathorized(nil)
return svc.NewErrAuthUnathorized(nil)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions api/routes/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func NewErrInvalidEntity(fields map[string]string) error {
return errors.Wrap(errors.WithData(ErrInvalidEntity, ErrDataInvalidEntity{Fields: fields}), nil)
}

// NewErrUnauthorized returns an error with the access is not authorized.
func NewErrUnauthorized(err error) error {
return errors.Wrap(ErrUnauthorized, err)
}
54 changes: 6 additions & 48 deletions api/services/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,11 @@ type AuthService interface {
AuthIsCacheToken(ctx context.Context, tenant, id string) (bool, error)
AuthUncacheToken(ctx context.Context, tenant, id string) error
AuthDevice(ctx context.Context, req requests.DeviceAuth, remoteAddr string) (*models.DeviceAuthResponse, error)
AuthUser(ctx context.Context, req requests.UserAuth) (*models.UserAuthResponse, error)
AuthGetToken(ctx context.Context, tenant string) (*models.UserAuthResponse, error)
AuthUser(ctx context.Context, req requests.UserAuth, validate bool) (*models.UserAuthResponse, error)
AuthGetToken(ctx context.Context, tenant string, mfa bool) (*models.UserAuthResponse, error)
AuthPublicKey(ctx context.Context, req requests.PublicKeyAuth) (*models.PublicKeyAuthResponse, error)
AuthSwapToken(ctx context.Context, ID, tenant string) (*models.UserAuthResponse, error)
AuthUserInfo(ctx context.Context, username, tenant, token string) (*models.UserAuthResponse, error)
AuthGetCacheMFA(ctx context.Context, username string) (bool, error)
AuthMFA(ctx context.Context, id string) (bool, error)
PublicKey() *rsa.PublicKey
}
Expand Down Expand Up @@ -138,7 +137,7 @@ func (s *service) AuthDevice(ctx context.Context, req requests.DeviceAuth, remot
}, nil
}

func (s *service) AuthUser(ctx context.Context, req requests.UserAuth) (*models.UserAuthResponse, error) {
func (s *service) AuthUser(ctx context.Context, req requests.UserAuth, validate bool) (*models.UserAuthResponse, error) {
var user *models.User
userFromUsername, errUsername := s.store.UserGetByUsername(ctx, strings.ToLower(req.Username))
userFromEmail, errEmail := s.store.UserGetByEmail(ctx, strings.ToLower(req.Username))
Expand Down Expand Up @@ -178,11 +177,6 @@ func (s *service) AuthUser(ctx context.Context, req requests.UserAuth) (*models.
return nil, NewErrUserNotFound(user.ID, err)
}

validate, err := s.AuthGetCacheMFA(ctx, user.Username)
if err != nil {
return nil, NewErrUserNotFound(user.ID, err)
}

password := sha256.Sum256([]byte(req.Password))
if user.Password == hex.EncodeToString(password[:]) {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, models.UserAuthClaims{
Expand Down Expand Up @@ -231,7 +225,7 @@ func (s *service) AuthUser(ctx context.Context, req requests.UserAuth) (*models.
return nil, NewErrAuthUnathorized(nil)
}

func (s *service) AuthGetToken(ctx context.Context, id string) (*models.UserAuthResponse, error) {
func (s *service) AuthGetToken(ctx context.Context, id string, mfa bool) (*models.UserAuthResponse, error) {
user, _, err := s.store.UserGetByID(ctx, id, false)
if err != nil {
return nil, NewErrUserNotFound(id, err)
Expand All @@ -258,11 +252,6 @@ func (s *service) AuthGetToken(ctx context.Context, id string) (*models.UserAuth
return nil, NewErrUserNotFound(id, err)
}

validate, err := s.AuthGetCacheMFA(ctx, user.Username)
if err != nil {
return nil, NewErrUserNotFound(user.ID, err)
}

token := jwt.NewWithClaims(jwt.SigningMethodRS256, models.UserAuthClaims{
Username: user.Username,
Admin: true,
Expand All @@ -274,7 +263,7 @@ func (s *service) AuthGetToken(ctx context.Context, id string) (*models.UserAuth
},
MFA: models.MFA{
Status: status,
Validate: validate,
Validate: mfa,
},
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(clock.Now().Add(time.Hour * 72)),
Expand All @@ -283,6 +272,7 @@ func (s *service) AuthGetToken(ctx context.Context, id string) (*models.UserAuth

tokenStr, err := token.SignedString(s.privKey)
if err != nil {

Check failure on line 274 in api/services/auth.go

View workflow job for this annotation

GitHub Actions / validate (api)

unnecessary leading newline (whitespace)

return nil, NewErrTokenSigned(err)
}

Expand Down Expand Up @@ -345,16 +335,6 @@ func (s *service) AuthSwapToken(ctx context.Context, id, tenant string) (*models
}
}

status, err := s.AuthMFA(ctx, user.ID)
if err != nil {
return nil, NewErrUserNotFound(id, err)
}

validate, err := s.AuthGetCacheMFA(ctx, user.Username)
if err != nil {
return nil, NewErrUserNotFound(user.ID, err)
}

for _, member := range namespace.Members {
if user.ID == member.ID {
token := jwt.NewWithClaims(jwt.SigningMethodRS256, models.UserAuthClaims{
Expand All @@ -366,10 +346,6 @@ func (s *service) AuthSwapToken(ctx context.Context, id, tenant string) (*models
AuthClaims: models.AuthClaims{
Claims: "user",
},
MFA: models.MFA{
Status: status,
Validate: validate,
},
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(clock.Now().Add(time.Hour * 72)),
},
Expand All @@ -390,7 +366,6 @@ func (s *service) AuthSwapToken(ctx context.Context, id, tenant string) (*models
Role: role,
Tenant: namespace.TenantID,
Email: user.Email,
MFA: status,
}, nil
}
}
Expand Down Expand Up @@ -475,23 +450,6 @@ func (s *service) AuthUncacheToken(ctx context.Context, tenant, id string) error
return s.cache.Delete(ctx, "token_"+tenant+id)
}

// AuthGetCacheMFA checks if the 'validate_mfa' status is cached;
//
// It receives a context, used to "control" the request flow and the user ID.
//
// AuthGetCacheMFA returns a string to indicate if the validate_mfa is cached and
//
// an error when it could not get the status of validate_mfa.
func (s *service) AuthGetCacheMFA(ctx context.Context, username string) (bool, error) {
var data bool

if err := s.cache.Get(ctx, "validate_mfa_"+username, &data); err != nil {
return false, err
}

return data, nil
}

func (s *service) AuthMFA(ctx context.Context, id string) (bool, error) {
status, err := s.store.GetStatusMFA(ctx, id)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion api/services/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func TestAuthUser(t *testing.T) {
assert.NoError(t, err)

service := NewService(store.Store(mock), privateKey, &privateKey.PublicKey, storecache.NewNullCache(), clientMock, nil)
authRes, err := service.AuthUser(ctx, tc.req)
authRes, err := service.AuthUser(ctx, tc.req, true)
assert.Equal(t, tc.expected, Expected{authRes, err})
})
}
Expand Down
6 changes: 0 additions & 6 deletions api/services/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ var (
ErrSessionNotFound = errors.New("session not found", ErrLayer, ErrCodeNotFound)
ErrAuthInvalid = errors.New("auth invalid", ErrLayer, ErrCodeInvalid)
ErrAuthUnathorized = errors.New("auth unauthorized", ErrLayer, ErrCodeUnauthorized)
ErrMFAUnathorized = errors.New("auth mfa unauthorized", ErrLayer, ErrCodeUnauthorized)
ErrNamespaceLimitReached = errors.New("namespace limit reached", ErrLayer, ErrCodeLimit)
ErrDeviceRemovedCount = errors.New("device removed count", ErrLayer, ErrCodeNotFound)
ErrDeviceRemovedInsert = errors.New("device removed insert", ErrLayer, ErrCodeStore)
Expand Down Expand Up @@ -379,11 +378,6 @@ func NewErrAuthUnathorized(err error) error {
return NewErrUnathorized(ErrAuthUnathorized, err)
}

// NewErrAuthUnathorized returns a error to be used when the auth is unauthorized.
func NewErrMFAUnathorized(err error) error {
return NewErrUnathorized(ErrAuthUnathorized, err)
}

// NewErrNamespaceLimitReached a error to be used when the user namespace limit number is reached.
func NewErrNamespaceLimitReached(limit int, err error) error {
return NewErrLimit(ErrNamespaceLimitReached, limit, err)
Expand Down
60 changes: 18 additions & 42 deletions api/services/mocks/services.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c1c3fd4

Please sign in to comment.