mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-05 20:54:18 +08:00
Merge pull request #3441 from gravitl/nmctl-access-token
feat: add support for user access tokens in nmctl
This commit is contained in:
parent
f9bc3a5386
commit
506f73ebb9
9 changed files with 185 additions and 7 deletions
43
cli/cmd/access_token/create.go
Normal file
43
cli/cmd/access_token/create.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package access_token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gravitl/netmaker/cli/functions"
|
||||||
|
"github.com/gravitl/netmaker/schema"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var accessTokenCreateCmd = &cobra.Command{
|
||||||
|
Use: "create [token-name]",
|
||||||
|
Short: "Create an access token",
|
||||||
|
Long: `Create an access token for a user`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
userName, _ := cmd.Flags().GetString("user")
|
||||||
|
expiresAt, _ := cmd.Flags().GetString("expires")
|
||||||
|
|
||||||
|
accessToken := &schema.UserAccessToken{}
|
||||||
|
accessToken.Name = args[0]
|
||||||
|
accessToken.UserName = userName
|
||||||
|
|
||||||
|
expTime := time.Now().Add(time.Hour * 24 * 365) // default to 1 year
|
||||||
|
if expiresAt != "" {
|
||||||
|
var err error
|
||||||
|
expTime, err = time.Parse(time.RFC3339, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
cmd.PrintErrf("Invalid expiration time format. Please use RFC3339 format (e.g. 2024-01-01T00:00:00Z). Using default 1 year.\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
accessToken.ExpiresAt = expTime
|
||||||
|
|
||||||
|
functions.PrettyPrint(functions.CreateAccessToken(accessToken))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
accessTokenCreateCmd.Flags().String("user", "", "Username to create token for")
|
||||||
|
accessTokenCreateCmd.Flags().String("expires", "", "Expiration time for the token in RFC3339 format (e.g. 2024-01-01T00:00:00Z). Defaults to 1 year from now.")
|
||||||
|
accessTokenCreateCmd.MarkFlagRequired("user")
|
||||||
|
rootCmd.AddCommand(accessTokenCreateCmd)
|
||||||
|
}
|
23
cli/cmd/access_token/delete.go
Normal file
23
cli/cmd/access_token/delete.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package access_token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gravitl/netmaker/cli/functions"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var accessTokenDeleteCmd = &cobra.Command{
|
||||||
|
Use: "delete [ACCESS TOKEN ID]",
|
||||||
|
Short: "Delete an access token",
|
||||||
|
Long: `Delete an access token by ID`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
functions.DeleteAccessToken(args[0])
|
||||||
|
fmt.Println("Access token deleted successfully")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(accessTokenDeleteCmd)
|
||||||
|
}
|
20
cli/cmd/access_token/get.go
Normal file
20
cli/cmd/access_token/get.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package access_token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gravitl/netmaker/cli/functions"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var accessTokenGetCmd = &cobra.Command{
|
||||||
|
Use: "get [USERNAME]",
|
||||||
|
Short: "Get a user's access token",
|
||||||
|
Long: `Get a user's access token`,
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
functions.PrettyPrint(functions.GetAccessToken(args[0]))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(accessTokenGetCmd)
|
||||||
|
}
|
28
cli/cmd/access_token/root.go
Normal file
28
cli/cmd/access_token/root.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package access_token
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "access_token",
|
||||||
|
Short: "Manage Netmaker user access tokens",
|
||||||
|
Long: `Manage a Netmaker user's access tokens. This command allows you to create, delete, and list access tokens for a user.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoot returns the root subcommand
|
||||||
|
func GetRoot() *cobra.Command {
|
||||||
|
return rootCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ var (
|
||||||
sso bool
|
sso bool
|
||||||
tenantId string
|
tenantId string
|
||||||
saas bool
|
saas bool
|
||||||
|
authToken string
|
||||||
)
|
)
|
||||||
|
|
||||||
var contextSetCmd = &cobra.Command{
|
var contextSetCmd = &cobra.Command{
|
||||||
|
@ -30,13 +31,14 @@ var contextSetCmd = &cobra.Command{
|
||||||
Username: username,
|
Username: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
MasterKey: masterKey,
|
MasterKey: masterKey,
|
||||||
|
AuthToken: authToken,
|
||||||
SSO: sso,
|
SSO: sso,
|
||||||
TenantId: tenantId,
|
TenantId: tenantId,
|
||||||
Saas: saas,
|
Saas: saas,
|
||||||
}
|
}
|
||||||
if !ctx.Saas {
|
if !ctx.Saas {
|
||||||
if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO {
|
if ctx.Username == "" && ctx.MasterKey == "" && !ctx.SSO && ctx.AuthToken == "" {
|
||||||
log.Fatal("Either username/password or master key is required")
|
log.Fatal("Either username/password or master key or auth token is required")
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
}
|
}
|
||||||
if ctx.Endpoint == "" {
|
if ctx.Endpoint == "" {
|
||||||
|
@ -49,8 +51,8 @@ var contextSetCmd = &cobra.Command{
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
}
|
}
|
||||||
ctx.Endpoint = fmt.Sprintf(functions.TenantUrlTemplate, tenantId)
|
ctx.Endpoint = fmt.Sprintf(functions.TenantUrlTemplate, tenantId)
|
||||||
if ctx.Username == "" && ctx.Password == "" && !ctx.SSO {
|
if ctx.Username == "" && ctx.Password == "" && ctx.AuthToken == "" && !ctx.SSO {
|
||||||
log.Fatal("Username/password is required for non-SSO SaaS contexts")
|
log.Fatal("Username/password or authtoken is required for non-SSO SaaS contexts")
|
||||||
cmd.Usage()
|
cmd.Usage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +64,7 @@ func init() {
|
||||||
contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
|
contextSetCmd.Flags().StringVar(&endpoint, "endpoint", "", "Endpoint of the API Server")
|
||||||
contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
|
contextSetCmd.Flags().StringVar(&username, "username", "", "Username")
|
||||||
contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
|
contextSetCmd.Flags().StringVar(&password, "password", "", "Password")
|
||||||
|
contextSetCmd.Flags().StringVar(&authToken, "auth_token", "", "Auth Token")
|
||||||
contextSetCmd.MarkFlagsRequiredTogether("username", "password")
|
contextSetCmd.MarkFlagsRequiredTogether("username", "password")
|
||||||
contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO)?")
|
contextSetCmd.Flags().BoolVar(&sso, "sso", false, "Login via Single Sign On (SSO)?")
|
||||||
contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
|
contextSetCmd.Flags().StringVar(&masterKey, "master_key", "", "Master Key")
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/gravitl/netmaker/cli/cmd/gateway"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/gravitl/netmaker/cli/cmd/access_token"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/acl"
|
"github.com/gravitl/netmaker/cli/cmd/acl"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/commons"
|
"github.com/gravitl/netmaker/cli/cmd/commons"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/context"
|
"github.com/gravitl/netmaker/cli/cmd/context"
|
||||||
|
@ -11,12 +11,14 @@ import (
|
||||||
"github.com/gravitl/netmaker/cli/cmd/enrollment_key"
|
"github.com/gravitl/netmaker/cli/cmd/enrollment_key"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/ext_client"
|
"github.com/gravitl/netmaker/cli/cmd/ext_client"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/failover"
|
"github.com/gravitl/netmaker/cli/cmd/failover"
|
||||||
|
"github.com/gravitl/netmaker/cli/cmd/gateway"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/host"
|
"github.com/gravitl/netmaker/cli/cmd/host"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/metrics"
|
"github.com/gravitl/netmaker/cli/cmd/metrics"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/network"
|
"github.com/gravitl/netmaker/cli/cmd/network"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/node"
|
"github.com/gravitl/netmaker/cli/cmd/node"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/server"
|
"github.com/gravitl/netmaker/cli/cmd/server"
|
||||||
"github.com/gravitl/netmaker/cli/cmd/user"
|
"github.com/gravitl/netmaker/cli/cmd/user"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,4 +59,5 @@ func init() {
|
||||||
rootCmd.AddCommand(enrollment_key.GetRoot())
|
rootCmd.AddCommand(enrollment_key.GetRoot())
|
||||||
rootCmd.AddCommand(failover.GetRoot())
|
rootCmd.AddCommand(failover.GetRoot())
|
||||||
rootCmd.AddCommand(gateway.GetRoot())
|
rootCmd.AddCommand(gateway.GetRoot())
|
||||||
|
rootCmd.AddCommand(access_token.GetRoot())
|
||||||
}
|
}
|
||||||
|
|
58
cli/functions/access_tokens.go
Normal file
58
cli/functions/access_tokens.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package functions
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gravitl/netmaker/models"
|
||||||
|
"github.com/gravitl/netmaker/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateAccessToken - creates an access token for a user
|
||||||
|
func CreateAccessToken(payload *schema.UserAccessToken) *models.SuccessfulUserLoginResponse {
|
||||||
|
res := request[models.SuccessResponse](http.MethodPost, "/api/v1/users/access_token", payload)
|
||||||
|
if res.Code != http.StatusOK {
|
||||||
|
log.Fatalf("Error creating access token: %s", res.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
var token models.SuccessfulUserLoginResponse
|
||||||
|
responseBytes, err := json.Marshal(res.Response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error marshaling response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(responseBytes, &token); err != nil {
|
||||||
|
log.Fatalf("Error unmarshaling token: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &token
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccessToken - fetch all access tokens per user
|
||||||
|
func GetAccessToken(userName string) []schema.UserAccessToken {
|
||||||
|
res := request[models.SuccessResponse](http.MethodGet, "/api/v1/users/access_token?username="+userName, nil)
|
||||||
|
if res.Code != http.StatusOK {
|
||||||
|
log.Fatalf("Error getting access token: %s", res.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokens []schema.UserAccessToken
|
||||||
|
responseBytes, err := json.Marshal(res.Response)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error marshaling response: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(responseBytes, &tokens); err != nil {
|
||||||
|
log.Fatalf("Error unmarshaling tokens: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAccessToken - delete an access token
|
||||||
|
func DeleteAccessToken(id string) {
|
||||||
|
res := request[models.SuccessResponse](http.MethodDelete, "/api/v1/users/access_token?id="+id, nil)
|
||||||
|
if res.Code != http.StatusOK {
|
||||||
|
log.Fatalf("Error deleting access token: %s", res.Message)
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,7 +192,7 @@ retry:
|
||||||
body := new(T)
|
body := new(T)
|
||||||
if len(resBodyBytes) > 0 {
|
if len(resBodyBytes) > 0 {
|
||||||
if err := json.Unmarshal(resBodyBytes, body); err != nil {
|
if err := json.Unmarshal(resBodyBytes, body); err != nil {
|
||||||
log.Fatalf("Error unmarshalling JSON: %s", err)
|
log.Fatalf("Error unmarshalling JSON: %s %s", err, string(resBodyBytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return body
|
return body
|
||||||
|
|
|
@ -163,7 +163,7 @@ func deleteUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
err := a.Get(r.Context())
|
err := a.Get(r.Context())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("id is required"), "badrequest"))
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("token does not exist"), "badrequest"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
caller, err := logic.GetUser(r.Header.Get("user"))
|
caller, err := logic.GetUser(r.Header.Get("user"))
|
||||||
|
|
Loading…
Add table
Reference in a new issue