horizoncd/horizon

View on GitHub
pkg/token/service/service.go

Summary

Maintainability
A
1 hr
Test Coverage
C
79%
// Copyright © 2023 Horizoncd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
 
package service
 
import (
"context"
"strings"
"time"
 
"github.com/golang-jwt/jwt/v4"
 
herror "github.com/horizoncd/horizon/core/errors"
tokenconfig "github.com/horizoncd/horizon/pkg/config/token"
perror "github.com/horizoncd/horizon/pkg/errors"
"github.com/horizoncd/horizon/pkg/param/managerparam"
"github.com/horizoncd/horizon/pkg/token/generator"
tokenmanager "github.com/horizoncd/horizon/pkg/token/manager"
tokenmodels "github.com/horizoncd/horizon/pkg/token/models"
)
 
type Service interface {
// CreateAccessToken used for personal access Token and resource access Token
CreateAccessToken(ctx context.Context, name, expiresAtStr string,
userID uint, scopes []string) (*tokenmodels.Token, error)
CreateJWTToken(subject string, expiresIn time.Duration, options ...ClaimsOption) (string, error)
ParseJWTToken(tokenStr string) (Claims, error)
}
 
func NewService(manager *managerparam.Manager, config tokenconfig.Config) Service {
return &service{
tokenManager: manager.TokenMgr,
TokenConfig: config,
}
}
 
type service struct {
tokenManager tokenmanager.Manager
TokenConfig tokenconfig.Config
}
 
Method `service.CreateAccessToken` has 5 return statements (exceeds 4 allowed).
func (s *service) CreateAccessToken(ctx context.Context, name, expiresAtStr string,
userID uint, scopes []string) (*tokenmodels.Token, error) {
// 1. check expiration date
createdAt := time.Now()
expiresIn := time.Duration(0)
if expiresAtStr != NeverExpire {
expiredAt, err := time.Parse(ExpiresAtFormat, expiresAtStr)
if err != nil {
return nil, perror.Wrapf(herror.ErrParamInvalid, "invalid expiration time, error: %s", err.Error())
}
if !expiredAt.After(createdAt) {
return nil, perror.Wrap(herror.ErrParamInvalid, "expiration time must be later than current time")
}
expiresIn = expiredAt.Sub(createdAt)
}
// 2. generate user access token
gen := generator.NewGeneralAccessTokenGenerator()
token, err := s.genAccessToken(gen, name, userID, scopes, createdAt, expiresIn)
if err != nil {
return nil, err
}
// 3. create token in db
token, err = s.tokenManager.CreateToken(ctx, token)
if err != nil {
return nil, err
}
return token, nil
}
 
Method `service.genAccessToken` has 6 arguments (exceeds 4 allowed). Consider refactoring.
func (s *service) genAccessToken(gen generator.CodeGenerator, name string, userID uint,
scopes []string, createdAt time.Time, expiresIn time.Duration) (*tokenmodels.Token, error) {
code := gen.Generate(&generator.CodeGenerateInfo{
Token: tokenmodels.Token{UserID: userID},
})
return &tokenmodels.Token{
Name: name,
Code: code,
Scope: strings.Join(scopes, " "),
CreatedAt: createdAt,
ExpiresIn: expiresIn,
UserID: userID,
}, nil
}
 
func (s *service) CreateJWTToken(subject string, expiresIn time.Duration, options ...ClaimsOption) (string, error) {
now := time.Now().UTC()
claims := &Claims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: ClaimsIssuer,
Subject: subject,
IssuedAt: jwt.NewNumericDate(now),
NotBefore: jwt.NewNumericDate(now),
},
}
if expiresIn > 0 {
expires := now.Add(expiresIn)
claims.ExpiresAt = jwt.NewNumericDate(expires)
}
 
for _, opt := range options {
opt(claims)
}
 
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(s.TokenConfig.JwtSigningKey))
}
 
// ParseJWTToken parses string and return claims
Method `service.ParseJWTToken` has 5 return statements (exceeds 4 allowed).
func (s *service) ParseJWTToken(tokenStr string) (Claims, error) {
var claims Claims
_, err := jwt.ParseWithClaims(tokenStr, &claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, perror.Wrapf(herror.ErrTokenInvalid,
"unexpected signing method: %v", token.Header["alg"])
}
return []byte(s.TokenConfig.JwtSigningKey), nil
})
if err != nil {
return Claims{}, err
}
 
if claims.Issuer != ClaimsIssuer {
return Claims{}, perror.Wrapf(herror.ErrTokenInvalid,
"unexpected claims issuer: %v", claims.Issuer)
}
return claims, nil
}