// Copyright 2023 Harness, Inc. // // 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 token import ( "context" "fmt" "time" "github.com/harness/gitness/app/jwt" "github.com/harness/gitness/app/store" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" "github.com/gotidy/ptr" ) const ( // userSessionTokenLifeTime is the duration a login / register token is valid. // NOTE: Users can list / delete session tokens via rest API if they want to cleanup earlier. userSessionTokenLifeTime time.Duration = 30 * 24 * time.Hour // 30 days. sessionTokenWithAccessPermissionsLifeTime time.Duration = 24 * time.Hour // 24 hours. ) func CreateUserWithAccessPermissions( user *types.User, accessPermissions *jwt.SubClaimsAccessPermissions, ) (string, error) { principal := user.ToPrincipal() return createWithAccessPermissions( principal, ptr.Duration(sessionTokenWithAccessPermissionsLifeTime), accessPermissions, ) } func CreateUserSession( ctx context.Context, tokenStore store.TokenStore, user *types.User, identifier string, ) (*types.Token, string, error) { principal := user.ToPrincipal() return create( ctx, tokenStore, enum.TokenTypeSession, principal, principal, identifier, ptr.Duration(userSessionTokenLifeTime), ) } func CreatePAT( ctx context.Context, tokenStore store.TokenStore, createdBy *types.Principal, createdFor *types.User, identifier string, lifetime *time.Duration, ) (*types.Token, string, error) { return create( ctx, tokenStore, enum.TokenTypePAT, createdBy, createdFor.ToPrincipal(), identifier, lifetime, ) } func CreateSAT( ctx context.Context, tokenStore store.TokenStore, createdBy *types.Principal, createdFor *types.ServiceAccount, identifier string, lifetime *time.Duration, ) (*types.Token, string, error) { return create( ctx, tokenStore, enum.TokenTypeSAT, createdBy, createdFor.ToPrincipal(), identifier, lifetime, ) } func create( ctx context.Context, tokenStore store.TokenStore, tokenType enum.TokenType, createdBy *types.Principal, createdFor *types.Principal, identifier string, lifetime *time.Duration, ) (*types.Token, string, error) { issuedAt := time.Now() var expiresAt *int64 if lifetime != nil { expiresAt = ptr.Int64(issuedAt.Add(*lifetime).UnixMilli()) } // create db entry first so we get the id. token := types.Token{ Type: tokenType, Identifier: identifier, PrincipalID: createdFor.ID, IssuedAt: issuedAt.UnixMilli(), ExpiresAt: expiresAt, CreatedBy: createdBy.ID, } err := tokenStore.Create(ctx, &token) if err != nil { return nil, "", fmt.Errorf("failed to store token in db: %w", err) } // create jwt token. jwtToken, err := jwt.GenerateForToken(&token, createdFor.Salt) if err != nil { return nil, "", fmt.Errorf("failed to create jwt token: %w", err) } return &token, jwtToken, nil } func createWithAccessPermissions( createdFor *types.Principal, lifetime *time.Duration, accessPermissions *jwt.SubClaimsAccessPermissions, ) (string, error) { jwtToken, err := jwt.GenerateForTokenWithAccessPermissions( createdFor.ID, lifetime, createdFor.Salt, accessPermissions, ) if err != nil { return "", fmt.Errorf("failed to create jwt token: %w", err) } return jwtToken, nil }