mirror of
				https://github.com/gravitl/netmaker.git
				synced 2025-10-31 12:16:29 +08:00 
			
		
		
		
	 307a3d1e4b
			
		
	
	307a3d1e4b
	
	
	
		
			
			* feat: api access tokens
* revoke all user tokens
* redefine access token api routes, add auto egress option to enrollment keys
* add server settings apis, add db table for settigs
* handle server settings updates
* switch to using settings from DB
* fix sever settings migration
* revet force migration for settings
* fix server settings database write
* egress model
* fix revoked tokens to be unauthorized
* update egress model
* remove unused functions
* convert access token to sql schema
* switch access token to sql schema
* fix merge conflicts
* fix server settings types
* bypass basic auth setting for super admin
* add TODO comment
* setup api handlers for egress revamp
* use single DB, fix update nat boolean field
* extend validaiton checks for egress ranges
* add migration to convert to new egress model
* fix panic interface conversion
* publish peer update on settings update
* revoke token generated by an user
* add user token creation restriction by user role
* add forbidden check for access token creation
* revoke user token when group or role is changed
* add default group to admin users on update
* chore(go): import style changes from migration branch;
1. Singular file names for table schema.
2. No table name method.
3. Use .Model instead of .Table.
4. No unnecessary tagging.
* remove nat check on egress gateway request
* Revert "remove nat check on egress gateway request"
This reverts commit 0aff12a189.
* remove nat check on egress gateway request
* feat(go): add db middleware;
* feat(go): restore method;
* feat(go): add user access token schema;
* add inet gw status to egress model
* fetch node ids in the tag, add inet gw info clients
* add inet gw info to node from egress list
* add migration logic internet gws
* create default acl policies
* add egress info
* add egress TODO
* add egress TODO
* fix user auth api:
* add reference id to acl policy
* add egress response from DB
* publish peer update on egress changes
* re initalise oauth and email config
* set verbosity
* normalise cidr on egress req
* add egress id to acl group
* change acls to use egress id
* resolve merge conflicts
* fix egress reference errors
* move egress model to schema
* add api context to DB
* sync auto update settings with hosts
* sync auto update settings with hosts
* check acl for egress node
* check for egress policy in the acl dst groups
* fix acl rules for egress policies with new models
* add status to egress model
* fix inet node func
* mask secret and convert jwt duration to minutes
* enable egress policies on creation
* convert jwt duration to minutes
* add relevant ranges to inet egress
* skip non active egress routes
* resolve merge conflicts
* fix static check
* update gorm tag for primary key on egress model
* create user policies for egress resources
* resolve merge conflicts
* get egress info on failover apis, add egress src validation for inet gws
* add additional validation checks on egress req
* add additional validation checks on egress req
* skip all resources for inet policy
* delete associated egress acl policies
* fix failover of inetclient
* avoid setting inet client asd inet gw
* fix all resource egress policy
* fix inet gw egress rule
* check for node egress on relay req
* fix egress acl rules comms
* add new field for egress info on node
* check acl policy in failover ctx
* avoid default host to be set as inet client
* fix relayed egress node
* add valid error messaging for egress validate func
* return if inet default host
* jump port detection to 51821
* check host ports on pull
* check user access gws via acls
* add validation check for default host and failover for inet clients
* add error messaging for acl policy check
* fix inet gw status
* ignore failover req for peer using inet gw
* check for allowed egress ranges for a peer
* add egress routes to static nodes by access
* avoid setting failvoer as inet client
* fix egress error messaging
* fix extclients egress comms
* fix inet gw acting as inet client
* return formatted error on update acl validation
* add default route for static nodes on inetclient
* check relay node acting as inetclient
* move inet node info to separate field, fix all resouces policy
* remove debug logs
---------
Co-authored-by: Vishal Dalwadi <dalwadivishal26@gmail.com>
		
	
		
			
				
	
	
		
			221 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package logic
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"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 startup
 | |
| func 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 out
 | |
| func 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 token
 | |
| func 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(),
 | |
| 		RacAutoDisable: GetRacAutoDisable() && (role != models.SuperAdminRole && role != models.AdminRole),
 | |
| 		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 token
 | |
| func CreateUserJWT(username string, role models.UserRoleID) (response string, err error) {
 | |
| 	settings := GetServerSettings()
 | |
| 	expirationTime := time.Now().Add(time.Duration(settings.JwtValidityDuration) * time.Minute)
 | |
| 	claims := &models.UserClaims{
 | |
| 		UserName:       username,
 | |
| 		Role:           role,
 | |
| 		TokenType:      models.UserIDTokenType,
 | |
| 		RacAutoDisable: settings.RacAutoDisable && (role != models.SuperAdminRole && role != models.AdminRole),
 | |
| 		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
 | |
| }
 | |
| 
 | |
| 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
 | |
| 	}
 | |
| 	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 APIS
 | |
| func 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] Only
 | |
| func 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
 | |
| }
 |