2022-07-29 20:37:09 +02:00
|
|
|
package http
|
2021-08-12 12:49:10 +02:00
|
|
|
|
|
|
|
import (
|
2024-07-03 11:33:02 +02:00
|
|
|
"context"
|
2021-08-12 12:49:10 +02:00
|
|
|
"encoding/json"
|
2023-02-03 21:47:20 +01:00
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2021-08-20 22:33:43 +02:00
|
|
|
"github.com/gorilla/mux"
|
2023-02-28 15:01:24 +01:00
|
|
|
|
2022-03-26 12:08:54 +01:00
|
|
|
"github.com/netbirdio/netbird/management/server"
|
2022-06-14 10:32:54 +02:00
|
|
|
"github.com/netbirdio/netbird/management/server/http/api"
|
2022-11-11 20:36:45 +01:00
|
|
|
"github.com/netbirdio/netbird/management/server/http/util"
|
2022-06-14 10:32:54 +02:00
|
|
|
"github.com/netbirdio/netbird/management/server/jwtclaims"
|
2022-11-11 20:36:45 +01:00
|
|
|
"github.com/netbirdio/netbird/management/server/status"
|
2021-08-12 12:49:10 +02:00
|
|
|
)
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// SetupKeysHandler is a handler that returns a list of setup keys of the account
|
|
|
|
type SetupKeysHandler struct {
|
2023-02-03 21:47:20 +01:00
|
|
|
accountManager server.AccountManager
|
|
|
|
claimsExtractor *jwtclaims.ClaimsExtractor
|
2021-08-12 12:49:10 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:46:08 +01:00
|
|
|
// NewSetupKeysHandler creates a new SetupKeysHandler HTTP handler
|
2023-02-28 15:01:24 +01:00
|
|
|
func NewSetupKeysHandler(accountManager server.AccountManager, authCfg AuthCfg) *SetupKeysHandler {
|
|
|
|
return &SetupKeysHandler{
|
2021-08-12 12:49:10 +02:00
|
|
|
accountManager: accountManager,
|
2023-02-03 21:47:20 +01:00
|
|
|
claimsExtractor: jwtclaims.NewClaimsExtractor(
|
|
|
|
jwtclaims.WithAudience(authCfg.Audience),
|
|
|
|
jwtclaims.WithUserIDClaim(authCfg.UserIDClaim),
|
|
|
|
),
|
2021-08-12 12:49:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// CreateSetupKey is a POST requests that creates a new SetupKey
|
|
|
|
func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2024-09-27 16:10:50 +02:00
|
|
|
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
2021-08-23 21:43:05 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2021-08-23 21:43:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-14 10:32:54 +02:00
|
|
|
req := &api.PostApiSetupKeysJSONRequestBody{}
|
2022-09-11 23:16:40 +02:00
|
|
|
err = json.NewDecoder(r.Body).Decode(&req)
|
2021-08-20 22:33:43 +02:00
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
2021-08-20 22:33:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-14 10:32:54 +02:00
|
|
|
if req.Name == "" {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "setup key name shouldn't be empty"), w)
|
2022-06-14 10:32:54 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if !(server.SetupKeyType(req.Type) == server.SetupKeyReusable ||
|
|
|
|
server.SetupKeyType(req.Type) == server.SetupKeyOneOff) {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "unknown setup key type %s", req.Type), w)
|
2021-08-22 11:29:25 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-06-14 10:32:54 +02:00
|
|
|
expiresIn := time.Duration(req.ExpiresIn) * time.Second
|
|
|
|
|
2024-10-28 17:52:23 +01:00
|
|
|
if expiresIn < 0 {
|
|
|
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "expiresIn can not be in the past"), w)
|
2023-08-04 23:54:51 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-09-11 23:16:40 +02:00
|
|
|
if req.AutoGroups == nil {
|
|
|
|
req.AutoGroups = []string{}
|
|
|
|
}
|
2022-11-11 20:36:45 +01:00
|
|
|
|
2023-09-04 11:37:39 +02:00
|
|
|
var ephemeral bool
|
|
|
|
if req.Ephemeral != nil {
|
|
|
|
ephemeral = *req.Ephemeral
|
|
|
|
}
|
2024-10-28 17:52:23 +01:00
|
|
|
|
2024-09-27 16:10:50 +02:00
|
|
|
setupKey, err := h.accountManager.CreateSetupKey(r.Context(), accountID, req.Name, server.SetupKeyType(req.Type), expiresIn,
|
|
|
|
req.AutoGroups, req.UsageLimit, userID, ephemeral)
|
2021-08-20 22:33:43 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2021-08-20 22:33:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-28 17:52:23 +01:00
|
|
|
apiSetupKeys := toResponseBody(setupKey)
|
|
|
|
// for the creation we need to send the plain key
|
|
|
|
apiSetupKeys.Key = setupKey.Key
|
|
|
|
|
|
|
|
util.WriteJSONObject(r.Context(), w, apiSetupKeys)
|
2021-08-20 22:33:43 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// GetSetupKey is a GET request to get a SetupKey by ID
|
|
|
|
func (h *SetupKeysHandler) GetSetupKey(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2024-09-27 16:10:50 +02:00
|
|
|
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
2021-12-27 13:17:15 +01:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2021-12-27 13:17:15 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-08-20 22:33:43 +02:00
|
|
|
vars := mux.Vars(r)
|
2023-05-03 00:15:25 +02:00
|
|
|
keyID := vars["keyId"]
|
2022-09-11 23:16:40 +02:00
|
|
|
if len(keyID) == 0 {
|
2024-10-28 17:52:23 +01:00
|
|
|
util.WriteError(r.Context(), status.NewInvalidKeyIDError(), w)
|
2021-08-20 22:33:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-27 16:10:50 +02:00
|
|
|
key, err := h.accountManager.GetSetupKey(r.Context(), accountID, userID, keyID)
|
2022-09-11 23:16:40 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2021-08-20 22:33:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-03 11:33:02 +02:00
|
|
|
writeSuccess(r.Context(), w, key)
|
2022-09-11 23:16:40 +02:00
|
|
|
}
|
2021-08-23 21:43:05 +02:00
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// UpdateSetupKey is a PUT request to update server.SetupKey
|
|
|
|
func (h *SetupKeysHandler) UpdateSetupKey(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2024-09-27 16:10:50 +02:00
|
|
|
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
2021-12-27 13:17:15 +01:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2021-12-27 13:17:15 +01:00
|
|
|
return
|
|
|
|
}
|
2021-08-23 21:43:05 +02:00
|
|
|
|
2022-09-11 23:16:40 +02:00
|
|
|
vars := mux.Vars(r)
|
2023-05-03 00:15:25 +02:00
|
|
|
keyID := vars["keyId"]
|
2022-09-11 23:16:40 +02:00
|
|
|
if len(keyID) == 0 {
|
2024-10-28 17:52:23 +01:00
|
|
|
util.WriteError(r.Context(), status.NewInvalidKeyIDError(), w)
|
2022-09-11 23:16:40 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-03 00:15:25 +02:00
|
|
|
req := &api.PutApiSetupKeysKeyIdJSONRequestBody{}
|
2022-09-11 23:16:40 +02:00
|
|
|
err = json.NewDecoder(r.Body).Decode(&req)
|
|
|
|
if err != nil {
|
2022-11-11 20:36:45 +01:00
|
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
2021-08-23 21:43:05 +02:00
|
|
|
return
|
2022-09-11 23:16:40 +02:00
|
|
|
}
|
2021-08-12 12:49:10 +02:00
|
|
|
|
2022-09-11 23:16:40 +02:00
|
|
|
if req.AutoGroups == nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "setup key AutoGroups field is invalid"), w)
|
2022-09-11 23:16:40 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
newKey := &server.SetupKey{}
|
|
|
|
newKey.AutoGroups = req.AutoGroups
|
|
|
|
newKey.Revoked = req.Revoked
|
|
|
|
newKey.Id = keyID
|
|
|
|
|
2024-09-27 16:10:50 +02:00
|
|
|
newKey, err = h.accountManager.SaveSetupKey(r.Context(), accountID, newKey, userID)
|
2022-09-11 23:16:40 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2022-09-11 23:16:40 +02:00
|
|
|
return
|
2021-08-12 12:49:10 +02:00
|
|
|
}
|
2024-07-03 11:33:02 +02:00
|
|
|
writeSuccess(r.Context(), w, newKey)
|
2022-09-11 23:16:40 +02:00
|
|
|
}
|
|
|
|
|
2023-02-28 15:01:24 +01:00
|
|
|
// GetAllSetupKeys is a GET request that returns a list of SetupKey
|
|
|
|
func (h *SetupKeysHandler) GetAllSetupKeys(w http.ResponseWriter, r *http.Request) {
|
2023-02-03 21:47:20 +01:00
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
2024-09-27 16:10:50 +02:00
|
|
|
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
2022-09-11 23:16:40 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2022-09-11 23:16:40 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-27 16:10:50 +02:00
|
|
|
setupKeys, err := h.accountManager.ListSetupKeys(r.Context(), accountID, userID)
|
2022-09-11 23:16:40 +02:00
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(r.Context(), err, w)
|
2022-09-11 23:16:40 +02:00
|
|
|
return
|
|
|
|
}
|
2022-11-11 20:36:45 +01:00
|
|
|
|
2022-09-11 23:16:40 +02:00
|
|
|
apiSetupKeys := make([]*api.SetupKey, 0)
|
|
|
|
for _, key := range setupKeys {
|
2022-11-05 10:24:50 +01:00
|
|
|
apiSetupKeys = append(apiSetupKeys, toResponseBody(key))
|
2022-09-11 23:16:40 +02:00
|
|
|
}
|
|
|
|
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteJSONObject(r.Context(), w, apiSetupKeys)
|
2021-08-12 12:49:10 +02:00
|
|
|
}
|
2021-08-20 22:33:43 +02:00
|
|
|
|
2024-10-28 17:52:23 +01:00
|
|
|
func (h *SetupKeysHandler) DeleteSetupKey(w http.ResponseWriter, r *http.Request) {
|
|
|
|
claims := h.claimsExtractor.FromRequestContext(r)
|
|
|
|
accountID, userID, err := h.accountManager.GetAccountIDFromToken(r.Context(), claims)
|
|
|
|
if err != nil {
|
|
|
|
util.WriteError(r.Context(), err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
keyID := vars["keyId"]
|
|
|
|
if len(keyID) == 0 {
|
|
|
|
util.WriteError(r.Context(), status.NewInvalidKeyIDError(), w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = h.accountManager.DeleteSetupKey(r.Context(), accountID, userID, keyID)
|
|
|
|
if err != nil {
|
|
|
|
util.WriteError(r.Context(), err, w)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
util.WriteJSONObject(r.Context(), w, emptyObject{})
|
|
|
|
}
|
|
|
|
|
2024-07-03 11:33:02 +02:00
|
|
|
func writeSuccess(ctx context.Context, w http.ResponseWriter, key *server.SetupKey) {
|
2021-08-20 22:33:43 +02:00
|
|
|
w.Header().Set("Content-Type", "application/json")
|
2024-04-25 21:20:24 +02:00
|
|
|
w.WriteHeader(200)
|
2021-08-20 22:33:43 +02:00
|
|
|
err := json.NewEncoder(w).Encode(toResponseBody(key))
|
|
|
|
if err != nil {
|
2024-07-03 11:33:02 +02:00
|
|
|
util.WriteError(ctx, err, w)
|
2021-08-20 22:33:43 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-14 10:32:54 +02:00
|
|
|
func toResponseBody(key *server.SetupKey) *api.SetupKey {
|
2021-08-25 14:16:17 +02:00
|
|
|
var state string
|
2023-11-27 16:40:02 +01:00
|
|
|
switch {
|
|
|
|
case key.IsExpired():
|
2021-08-25 14:16:17 +02:00
|
|
|
state = "expired"
|
2023-11-27 16:40:02 +01:00
|
|
|
case key.IsRevoked():
|
2021-08-25 14:16:17 +02:00
|
|
|
state = "revoked"
|
2023-11-27 16:40:02 +01:00
|
|
|
case key.IsOverUsed():
|
2021-08-25 14:16:17 +02:00
|
|
|
state = "overused"
|
2023-11-27 16:40:02 +01:00
|
|
|
default:
|
2021-08-25 14:16:17 +02:00
|
|
|
state = "valid"
|
|
|
|
}
|
2022-09-11 23:16:40 +02:00
|
|
|
|
2022-06-14 10:32:54 +02:00
|
|
|
return &api.SetupKey{
|
2022-09-11 23:16:40 +02:00
|
|
|
Id: key.Id,
|
2024-10-28 17:52:23 +01:00
|
|
|
Key: key.KeySecret,
|
2022-09-11 23:16:40 +02:00
|
|
|
Name: key.Name,
|
|
|
|
Expires: key.ExpiresAt,
|
|
|
|
Type: string(key.Type),
|
|
|
|
Valid: key.IsValid(),
|
|
|
|
Revoked: key.Revoked,
|
|
|
|
UsedTimes: key.UsedTimes,
|
|
|
|
LastUsed: key.LastUsed,
|
|
|
|
State: state,
|
|
|
|
AutoGroups: key.AutoGroups,
|
|
|
|
UpdatedAt: key.UpdatedAt,
|
2022-12-05 13:09:59 +01:00
|
|
|
UsageLimit: key.UsageLimit,
|
2023-09-06 10:40:45 +02:00
|
|
|
Ephemeral: key.Ephemeral,
|
2021-08-20 22:33:43 +02:00
|
|
|
}
|
|
|
|
}
|