-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(COR-981): add admin command to manage JWT for internal usage
- Loading branch information
Showing
8 changed files
with
335 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package cmd | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"github.com/go-jose/go-jose/v4/json" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"io" | ||
"net/http" | ||
"os" | ||
"text/tabwriter" | ||
|
||
"github.com/qovery/qovery-cli/utils" | ||
) | ||
|
||
var ( | ||
adminJwtForQoveryUsageCreateCmd = &cobra.Command{ | ||
Use: "create", | ||
Short: "Create a Jwt for Qovery usage", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
createJwtForQoveryUsage() | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
adminJwtForQoveryUsageCreateCmd.Flags().StringVarP(&clusterId, "cluster-id", "c", "", "Cluster's id") | ||
adminJwtForQoveryUsageCreateCmd.Flags().StringVarP(&organizationId, "organization-id", "", "", "Organization's id") | ||
adminJwtForQoveryUsageCreateCmd.Flags().StringVarP(&rootDns, "root-dns", "", "", "root dns") | ||
adminJwtForQoveryUsageCreateCmd.Flags().StringVarP(&additionalClaims, "additional-claims", "", "{}", "Additional claims in JSON format (e.g., '{\"key1\":\"value1\",\"key2\":\"value2\"}')") | ||
adminJwtForQoveryUsageCreateCmd.Flags().StringVarP(&description, "description", "d", "", "Description of the JWT") | ||
|
||
adminJwtForQoveryUsageCmd.AddCommand(adminJwtForQoveryUsageCreateCmd) | ||
} | ||
|
||
func createJwtForQoveryUsage() { | ||
utils.CheckAdminUrl() | ||
|
||
tokenType, token, err := utils.GetAccessToken() | ||
if err != nil { | ||
utils.PrintlnError(err) | ||
os.Exit(0) | ||
} | ||
|
||
var claimsMap map[string]string | ||
err = json.Unmarshal([]byte(additionalClaims), &claimsMap) | ||
if err != nil { | ||
fmt.Printf("Error when parsing additional-claims : %v\n", err) | ||
return | ||
} | ||
|
||
type Payload struct { | ||
OrganizationId string `json:"organization_id"` | ||
ClusterId string `json:"cluster_id"` | ||
RootDns string `json:"root_dns"` | ||
AdditionalClaims map[string]string `json:"additional_claims"` | ||
Description string `json:"description"` | ||
} | ||
|
||
var payload, _ = json.Marshal(Payload{ | ||
ClusterId: clusterId, | ||
OrganizationId: organizationId, | ||
RootDns: rootDns, | ||
AdditionalClaims: claimsMap, | ||
Description: description, | ||
}) | ||
|
||
url := fmt.Sprintf("%s/jwts", utils.AdminUrl) | ||
req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(payload)) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
req.Header.Set("Authorization", utils.GetAuthorizationHeaderValue(tokenType, token)) | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
res, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
body, _ := io.ReadAll(res.Body) | ||
if res.StatusCode != http.StatusOK { | ||
utils.PrintlnError(fmt.Errorf("error uploading debug logs: %s %s", res.Status, body)) | ||
return | ||
} | ||
|
||
jwtForQoveryUsage := struct { | ||
KeyId string `json:"key_id"` | ||
Description string `json:"description"` | ||
Jwt string `json:"decrypted_jwt"` | ||
CreatedAt string `json:"created_at"` | ||
}{} | ||
|
||
if err := json.Unmarshal(body, &jwtForQoveryUsage); err != nil { | ||
log.Fatal(err) | ||
} | ||
_, jwtPayload, err := DecodeJWT(jwtForQoveryUsage.Jwt) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) | ||
|
||
_, _ = fmt.Fprintln(w, "Field\t | Value") | ||
_, _ = fmt.Fprintln(w, "------\t | ------") | ||
|
||
_, _ = fmt.Fprintf(w, "key_id\t | %s\n", jwtForQoveryUsage.KeyId) | ||
_, _ = fmt.Fprintf(w, "description\t | %s\n", jwtForQoveryUsage.Description) | ||
_, _ = fmt.Fprintf(w, "jwt payload\t | %s\n", jwtPayload) | ||
_, _ = fmt.Fprintf(w, "jwt\t | %s\n", jwtForQoveryUsage.Jwt) | ||
_, _ = fmt.Fprintf(w, "created_at\t | %s\n", jwtForQoveryUsage.CreatedAt) | ||
_ = w.Flush() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/qovery/qovery-cli/utils" | ||
) | ||
|
||
var ( | ||
adminJwtForQoveryUsageDeleteCmd = &cobra.Command{ | ||
Use: "delete", | ||
Short: "Delete a Jwt for Qovery Usage", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
deleteJwtForQoveryUsage() | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
adminJwtForQoveryUsageDeleteCmd.Flags().StringVarP(&jwtKid, "kid", "", "", "Cluster's id") | ||
|
||
adminJwtForQoveryUsageCmd.AddCommand(adminJwtForQoveryUsageDeleteCmd) | ||
|
||
} | ||
|
||
func deleteJwtForQoveryUsage() { | ||
utils.CheckAdminUrl() | ||
|
||
tokenType, token, err := utils.GetAccessToken() | ||
if err != nil { | ||
utils.PrintlnError(err) | ||
os.Exit(0) | ||
} | ||
|
||
url := fmt.Sprintf("%s/jwts/%s", utils.AdminUrl, jwtKid) | ||
req, err := http.NewRequest(http.MethodDelete, url, nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
req.Header.Set("Authorization", utils.GetAuthorizationHeaderValue(tokenType, token)) | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
res, err := http.DefaultClient.Do(req) | ||
if res == nil { | ||
utils.PrintlnError(fmt.Errorf("error sending delete HTTP request")) | ||
return | ||
} | ||
|
||
if res.StatusCode != http.StatusNoContent { | ||
utils.PrintlnError(fmt.Errorf("error: %s", res.Status)) | ||
return | ||
} | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package cmd | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"github.com/golang-jwt/jwt/v5" | ||
log "github.com/sirupsen/logrus" | ||
"github.com/spf13/cobra" | ||
"io" | ||
"net/http" | ||
"os" | ||
"text/tabwriter" | ||
|
||
"github.com/qovery/qovery-cli/utils" | ||
) | ||
|
||
var ( | ||
adminJwtForQoveryUsageListCmd = &cobra.Command{ | ||
Use: "list", | ||
Short: "List Jwt for Qovery usage", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
listJwtsForQoveryUsage() | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
adminJwtForQoveryUsageListCmd.Flags() | ||
|
||
adminJwtForQoveryUsageCmd.AddCommand(adminJwtForQoveryUsageListCmd) | ||
|
||
} | ||
|
||
func listJwtsForQoveryUsage() { | ||
utils.CheckAdminUrl() | ||
|
||
tokenType, token, err := utils.GetAccessToken() | ||
if err != nil { | ||
utils.PrintlnError(err) | ||
os.Exit(0) | ||
} | ||
|
||
url := fmt.Sprintf("%s/jwts", utils.AdminUrl) | ||
req, err := http.NewRequest(http.MethodGet, url, nil) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
req.Header.Set("Authorization", utils.GetAuthorizationHeaderValue(tokenType, token)) | ||
req.Header.Set("Content-Type", "application/json") | ||
|
||
res, err := http.DefaultClient.Do(req) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
body, _ := io.ReadAll(res.Body) | ||
if res.StatusCode != http.StatusOK { | ||
utils.PrintlnError(fmt.Errorf("error uploading debug logs: %s %s", res.Status, body)) | ||
return | ||
} | ||
|
||
resp := struct { | ||
Results []struct { | ||
KeyId string `json:"key_id"` | ||
Description string `json:"description"` | ||
Jwt string `json:"decrypted_jwt"` | ||
CreatedAt string `json:"created_at"` | ||
} `json:"results"` | ||
}{} | ||
if err := json.Unmarshal(body, &resp); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0) | ||
format := "%s\t | %s\t | %s\t | %s\t | %s\n" | ||
_, _ = fmt.Fprintf(w, format, "", "key_id", "descripton", "jwt payload", "created_at") | ||
for idx, jwtForQoveryUsage := range resp.Results { | ||
_, jwtPayload, err := DecodeJWT(jwtForQoveryUsage.Jwt) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
_, _ = fmt.Fprintln(w, "Field\t | Value") | ||
_, _ = fmt.Fprintln(w, "------\t | ------") | ||
|
||
_, _ = fmt.Fprintf(w, "index\t | %s\n", fmt.Sprintf("%d", idx+1)) | ||
_, _ = fmt.Fprintf(w, "key_id\t | %s\n", jwtForQoveryUsage.KeyId) | ||
_, _ = fmt.Fprintf(w, "description\t | %s\n", jwtForQoveryUsage.Description) | ||
_, _ = fmt.Fprintf(w, "jwt payload\t | %s\n", jwtPayload) | ||
_, _ = fmt.Fprintf(w, "jwt\t | %s\n", jwtForQoveryUsage.Jwt) | ||
_, _ = fmt.Fprintf(w, "created_at\t | %s\n", jwtForQoveryUsage.CreatedAt) | ||
} | ||
_ = w.Flush() | ||
} | ||
|
||
func DecodeJWT(tokenString string) (string, string, error) { | ||
token, _, err := new(jwt.Parser).ParseUnverified(tokenString, jwt.MapClaims{}) | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to parse token: %w", err) | ||
} | ||
|
||
headerJSON, err := json.Marshal(token.Header) | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to marshal header: %w", err) | ||
} | ||
|
||
claimsJSON, err := json.Marshal(token.Claims) | ||
if err != nil { | ||
return "", "", fmt.Errorf("failed to marshal claims: %w", err) | ||
} | ||
|
||
return string(headerJSON), string(claimsJSON), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package cmd | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/spf13/cobra" | ||
|
||
"github.com/qovery/qovery-cli/utils" | ||
) | ||
|
||
var ( | ||
adminJwtForQoveryUsageCmd = &cobra.Command{ | ||
Use: "jwt-qovery-usage", | ||
Short: "Manage JWT for qovery usage ", | ||
Run: func(cmd *cobra.Command, args []string) { | ||
utils.Capture(cmd) | ||
|
||
if len(args) == 0 { | ||
_ = cmd.Help() | ||
os.Exit(0) | ||
} | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
adminCmd.AddCommand(adminJwtForQoveryUsageCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters