| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 | package logicimport (	"context"	"crypto/hmac"	"crypto/sha256"	"encoding/hex"	"errors"	"fmt"	"strings"	"time"	"github.com/golang-jwt/jwt/v4"	"github.com/gravitl/netmaker/db"	"github.com/gravitl/netmaker/logger"	"github.com/gravitl/netmaker/models"	"github.com/gravitl/netmaker/schema"	"github.com/gravitl/netmaker/servercfg")var jwtSecretKey []byte// SetJWTSecret - sets the jwt secret on server startupfunc SetJWTSecret() {	currentSecret, jwtErr := FetchJWTSecret()	if jwtErr != nil {		newValue := RandomString(64)		jwtSecretKey = []byte(newValue) // 512 bit random password		if err := StoreJWTSecret(string(jwtSecretKey)); err != nil {			logger.FatalLog("something went wrong when configuring JWT authentication")		}	} else {		jwtSecretKey = []byte(currentSecret)	}}// CreateJWT func will used to create the JWT while signing in and signing outfunc CreateJWT(uuid string, macAddress string, network string) (response string, err error) {	expirationTime := time.Now().Add(15 * time.Minute)	claims := &models.Claims{		ID:         uuid,		Network:    network,		MacAddress: macAddress,		RegisteredClaims: jwt.RegisteredClaims{			Issuer:    "Netmaker",			Subject:   fmt.Sprintf("node|%s", uuid),			IssuedAt:  jwt.NewNumericDate(time.Now()),			ExpiresAt: jwt.NewNumericDate(expirationTime),		},	}	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)	tokenString, err := token.SignedString(jwtSecretKey)	if err == nil {		return tokenString, nil	}	return "", err}// CreateUserJWT - creates a user jwt tokenfunc CreateUserAccessJwtToken(username string, role models.UserRoleID, d time.Time, tokenID string) (response string, err error) {	claims := &models.UserClaims{		UserName:  username,		Role:      role,		TokenType: models.AccessTokenType,		Api:       servercfg.GetAPIHost(),		RegisteredClaims: jwt.RegisteredClaims{			Issuer:    "Netmaker",			Subject:   fmt.Sprintf("user|%s", username),			IssuedAt:  jwt.NewNumericDate(time.Now()),			ExpiresAt: jwt.NewNumericDate(d),			ID:        tokenID,		},	}	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)	tokenString, err := token.SignedString(jwtSecretKey)	if err == nil {		return tokenString, nil	}	return "", err}// CreateUserJWT - creates a user jwt tokenfunc CreateUserJWT(username string, role models.UserRoleID, appName string) (response string, err error) {	duration := GetJwtValidityDuration()	if appName == NetclientApp || appName == NetmakerDesktopApp {		duration = GetJwtValidityDurationForClients()	}	expirationTime := time.Now().Add(duration)	claims := &models.UserClaims{		UserName:  username,		Role:      role,		TokenType: models.UserIDTokenType,		RegisteredClaims: jwt.RegisteredClaims{			Issuer:    "Netmaker",			Subject:   fmt.Sprintf("user|%s", username),			IssuedAt:  jwt.NewNumericDate(time.Now()),			ExpiresAt: jwt.NewNumericDate(expirationTime),		},	}	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)	tokenString, err := token.SignedString(jwtSecretKey)	if err == nil {		return tokenString, nil	}	return "", err}// CreatePreAuthToken generate a jwt token to be used as intermediate// token after primary-factor authentication but before secondary-factor// authentication.func CreatePreAuthToken(username string) (string, error) {	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{		Issuer:    "Netmaker",		Subject:   username,		Audience:  []string{"auth:mfa"},		IssuedAt:  jwt.NewNumericDate(time.Now()),		ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),	})	return token.SignedString(jwtSecretKey)}func GenerateOTPAuthURLSignature(url string) string {	signer := hmac.New(sha256.New, jwtSecretKey)	signer.Write([]byte(url))	return hex.EncodeToString(signer.Sum(nil))}func VerifyOTPAuthURL(url, signature string) bool {	signatureBytes, err := hex.DecodeString(signature)	if err != nil {		return false	}	signer := hmac.New(sha256.New, jwtSecretKey)	signer.Write([]byte(url))	return hmac.Equal(signatureBytes, signer.Sum(nil))}func GetUserNameFromToken(authtoken string) (username string, err error) {	claims := &models.UserClaims{}	var tokenSplit = strings.Split(authtoken, " ")	var tokenString = ""	if len(tokenSplit) < 2 {		return "", Unauthorized_Err	} else {		tokenString = tokenSplit[1]	}	if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {		return MasterUser, nil	}	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {		return jwtSecretKey, nil	})	if err != nil {		return "", Unauthorized_Err	}	for _, aud := range claims.Audience {		// token created for mfa cannot be used for		// anything else.		if aud == "auth:mfa" {			return "", Unauthorized_Err		}	}	if claims.TokenType == models.AccessTokenType {		jti := claims.ID		if jti != "" {			a := schema.UserAccessToken{ID: jti}			// check if access token is active			err := a.Get(db.WithContext(context.TODO()))			if err != nil {				err = errors.New("token revoked")				return "", err			}			a.LastUsed = time.Now().UTC()			a.Update(db.WithContext(context.TODO()))		}	}	if token != nil && token.Valid {		var user *models.User		// check that user exists		user, err = GetUser(claims.UserName)		if err != nil {			return "", err		}		if user.UserName != "" {			return user.UserName, nil		}		if user.PlatformRoleID != claims.Role {			return "", Unauthorized_Err		}		err = errors.New("user does not exist")	} else {		err = Unauthorized_Err	}	return "", err}// VerifyUserToken func will used to Verify the JWT Token while using APISfunc VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin bool, err error) {	claims := &models.UserClaims{}	if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {		return MasterUser, true, true, nil	}	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {		return jwtSecretKey, nil	})	if claims.TokenType == models.AccessTokenType {		jti := claims.ID		if jti != "" {			a := schema.UserAccessToken{ID: jti}			// check if access token is active			err := a.Get(db.WithContext(context.TODO()))			if err != nil {				err = errors.New("token revoked")				return "", false, false, err			}			a.LastUsed = time.Now().UTC()			a.Update(db.WithContext(context.TODO()))		}	}	if token != nil && token.Valid {		var user *models.User		// check that user exists		user, err = GetUser(claims.UserName)		if err != nil {			return "", false, false, err		}		if user.UserName != "" {			return user.UserName, user.PlatformRoleID == models.SuperAdminRole,				user.PlatformRoleID == models.AdminRole, nil		}		err = errors.New("user does not exist")	}	return "", false, false, err}// VerifyHostToken - [hosts] Onlyfunc VerifyHostToken(tokenString string) (hostID string, mac string, network string, err error) {	claims := &models.Claims{}	// this may be a stupid way of serving up a master key	// TODO: look into a different method. Encryption?	if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {		return "mastermac", "", "", nil	}	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {		return jwtSecretKey, nil	})	if token != nil {		return claims.ID, claims.MacAddress, claims.Network, nil	}	return "", "", "", err}
 |