Skip to content

Commit

Permalink
[management] Setup key improvements (#2775)
Browse files Browse the repository at this point in the history
  • Loading branch information
pascal-fischer authored Oct 28, 2024
1 parent 1e44c5b commit 10480eb
Show file tree
Hide file tree
Showing 22 changed files with 549 additions and 195 deletions.
1 change: 1 addition & 0 deletions management/server/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ type AccountManager interface {
FindExistingPostureCheck(accountID string, checks *posture.ChecksDefinition) (*posture.Checks, error)
GetAccountIDForPeerKey(ctx context.Context, peerKey string) (string, error)
GetAccountSettings(ctx context.Context, accountID string, userID string) (*Settings, error)
DeleteSetupKey(ctx context.Context, accountID, userID, keyID string) error
}

type DefaultAccountManager struct {
Expand Down
9 changes: 0 additions & 9 deletions management/server/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1010,7 +1010,6 @@ func TestAccountManager_AddPeer(t *testing.T) {
return
}
expectedPeerKey := key.PublicKey().String()
expectedSetupKey := setupKey.Key

peer, _, _, err := manager.AddPeer(context.Background(), setupKey.Key, "", &nbpeer.Peer{
Key: expectedPeerKey,
Expand All @@ -1035,10 +1034,6 @@ func TestAccountManager_AddPeer(t *testing.T) {
t.Errorf("expecting just added peer's IP %s to be in a network range %s", peer.IP.String(), account.Network.Net.String())
}

if peer.SetupKey != expectedSetupKey {
t.Errorf("expecting just added peer to have SetupKey = %s, got %s", expectedSetupKey, peer.SetupKey)
}

if account.Network.CurrentSerial() != 1 {
t.Errorf("expecting Network Serial=%d to be incremented by 1 and be equal to %d when adding new peer to account", serial, account.Network.CurrentSerial())
}
Expand Down Expand Up @@ -2367,15 +2362,13 @@ func TestAccount_GetNextPeerExpiration(t *testing.T) {
LoginExpired: false,
},
LoginExpirationEnabled: true,
SetupKey: "key",
},
"peer-2": {
Status: &nbpeer.PeerStatus{
Connected: true,
LoginExpired: false,
},
LoginExpirationEnabled: true,
SetupKey: "key",
},
},
expiration: time.Second,
Expand Down Expand Up @@ -2529,15 +2522,13 @@ func TestAccount_GetNextInactivePeerExpiration(t *testing.T) {
LoginExpired: false,
},
InactivityExpirationEnabled: true,
SetupKey: "key",
},
"peer-2": {
Status: &nbpeer.PeerStatus{
Connected: true,
LoginExpired: false,
},
InactivityExpirationEnabled: true,
SetupKey: "key",
},
},
expiration: time.Second,
Expand Down
3 changes: 3 additions & 0 deletions management/server/activity/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ const (
AccountPeerInactivityExpirationEnabled Activity = 65
AccountPeerInactivityExpirationDisabled Activity = 66
AccountPeerInactivityExpirationDurationUpdated Activity = 67

SetupKeyDeleted Activity = 68
)

var activityMap = map[Activity]Code{
Expand Down Expand Up @@ -219,6 +221,7 @@ var activityMap = map[Activity]Code{
AccountPeerInactivityExpirationEnabled: {"Account peer inactivity expiration enabled", "account.peer.inactivity.expiration.enable"},
AccountPeerInactivityExpirationDisabled: {"Account peer inactivity expiration disabled", "account.peer.inactivity.expiration.disable"},
AccountPeerInactivityExpirationDurationUpdated: {"Account peer inactivity expiration duration updated", "account.peer.inactivity.expiration.update"},
SetupKeyDeleted: {"Setup key deleted", "setupkey.delete"},
}

// StringCode returns a string code of the activity
Expand Down
31 changes: 28 additions & 3 deletions management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,9 @@ components:
type: string
example: reusable
expires_in:
description: Expiration time in seconds
description: Expiration time in seconds, 0 will mean the key never expires
type: integer
minimum: 86400
maximum: 31536000
minimum: 0
example: 86400
revoked:
description: Setup key revocation status
Expand Down Expand Up @@ -2018,6 +2017,32 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
delete:
summary: Delete a Setup Key
description: Delete a Setup Key
tags: [ Setup Keys ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: keyId
required: true
schema:
type: string
description: The unique identifier of a setup key
responses:
'200':
description: Delete status code
content: { }
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/groups:
get:
summary: List all Groups
Expand Down
2 changes: 1 addition & 1 deletion management/server/http/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions management/server/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ func (apiHandler *apiHandler) addSetupKeysEndpoint() {
apiHandler.Router.HandleFunc("/setup-keys", keysHandler.CreateSetupKey).Methods("POST", "OPTIONS")
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.GetSetupKey).Methods("GET", "OPTIONS")
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.UpdateSetupKey).Methods("PUT", "OPTIONS")
apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.DeleteSetupKey).Methods("DELETE", "OPTIONS")
}

func (apiHandler *apiHandler) addPoliciesEndpoint() {
Expand Down
4 changes: 2 additions & 2 deletions management/server/http/peers_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ import (
"time"

"github.com/gorilla/mux"
"golang.org/x/exp/maps"

"github.com/netbirdio/netbird/management/server"
nbgroup "github.com/netbirdio/netbird/management/server/group"
"github.com/netbirdio/netbird/management/server/http/api"
"github.com/netbirdio/netbird/management/server/jwtclaims"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"golang.org/x/exp/maps"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -168,7 +169,6 @@ func TestGetPeers(t *testing.T) {
peer := &nbpeer.Peer{
ID: testPeerID,
Key: "key",
SetupKey: "setupkey",
IP: net.ParseIP("100.64.0.1"),
Status: &nbpeer.PeerStatus{Connected: true},
Name: "PeerName",
Expand Down
43 changes: 35 additions & 8 deletions management/server/http/setupkeys_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request

expiresIn := time.Duration(req.ExpiresIn) * time.Second

day := time.Hour * 24
year := day * 365
if expiresIn < day || expiresIn > year {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "expiresIn should be between 1 day and 365 days"), w)
if expiresIn < 0 {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "expiresIn can not be in the past"), w)
return
}

Expand All @@ -76,14 +74,19 @@ func (h *SetupKeysHandler) CreateSetupKey(w http.ResponseWriter, r *http.Request
if req.Ephemeral != nil {
ephemeral = *req.Ephemeral
}

setupKey, err := h.accountManager.CreateSetupKey(r.Context(), accountID, req.Name, server.SetupKeyType(req.Type), expiresIn,
req.AutoGroups, req.UsageLimit, userID, ephemeral)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}

writeSuccess(r.Context(), w, setupKey)
apiSetupKeys := toResponseBody(setupKey)
// for the creation we need to send the plain key
apiSetupKeys.Key = setupKey.Key

util.WriteJSONObject(r.Context(), w, apiSetupKeys)
}

// GetSetupKey is a GET request to get a SetupKey by ID
Expand All @@ -98,7 +101,7 @@ func (h *SetupKeysHandler) GetSetupKey(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
keyID := vars["keyId"]
if len(keyID) == 0 {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid key ID"), w)
util.WriteError(r.Context(), status.NewInvalidKeyIDError(), w)
return
}

Expand All @@ -123,7 +126,7 @@ func (h *SetupKeysHandler) UpdateSetupKey(w http.ResponseWriter, r *http.Request
vars := mux.Vars(r)
keyID := vars["keyId"]
if len(keyID) == 0 {
util.WriteError(r.Context(), status.Errorf(status.InvalidArgument, "invalid key ID"), w)
util.WriteError(r.Context(), status.NewInvalidKeyIDError(), w)
return
}

Expand Down Expand Up @@ -181,6 +184,30 @@ func (h *SetupKeysHandler) GetAllSetupKeys(w http.ResponseWriter, r *http.Reques
util.WriteJSONObject(r.Context(), w, apiSetupKeys)
}

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{})
}

func writeSuccess(ctx context.Context, w http.ResponseWriter, key *server.SetupKey) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
Expand All @@ -206,7 +233,7 @@ func toResponseBody(key *server.SetupKey) *api.SetupKey {

return &api.SetupKey{
Id: key.Id,
Key: key.Key,
Key: key.KeySecret,
Name: key.Name,
Expires: key.ExpiresAt,
Type: string(key.Type),
Expand Down
25 changes: 22 additions & 3 deletions management/server/http/setupkeys_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
ListSetupKeysFunc: func(_ context.Context, accountID, userID string) ([]*server.SetupKey, error) {
return []*server.SetupKey{defaultKey}, nil
},

DeleteSetupKeyFunc: func(_ context.Context, accountID, userID, keyID string) error {
if keyID == defaultKey.Id {
return nil
}
return status.Errorf(status.NotFound, "key %s not found", keyID)
},
},
claimsExtractor: jwtclaims.NewClaimsExtractor(
jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims {
Expand All @@ -81,18 +88,21 @@ func initSetupKeysTestMetaData(defaultKey *server.SetupKey, newKey *server.Setup
}

func TestSetupKeysHandlers(t *testing.T) {
defaultSetupKey := server.GenerateDefaultSetupKey()
defaultSetupKey, _ := server.GenerateDefaultSetupKey()
defaultSetupKey.Id = existingSetupKeyID

adminUser := server.NewAdminUser("test_user")

newSetupKey := server.GenerateSetupKey(newSetupKeyName, server.SetupKeyReusable, 0, []string{"group-1"},
newSetupKey, plainKey := server.GenerateSetupKey(newSetupKeyName, server.SetupKeyReusable, 0, []string{"group-1"},
server.SetupKeyUnlimitedUsage, true)
newSetupKey.Key = plainKey
updatedDefaultSetupKey := defaultSetupKey.Copy()
updatedDefaultSetupKey.AutoGroups = []string{"group-1"}
updatedDefaultSetupKey.Name = updatedSetupKeyName
updatedDefaultSetupKey.Revoked = true

expectedNewKey := toResponseBody(newSetupKey)
expectedNewKey.Key = plainKey
tt := []struct {
name string
requestType string
Expand Down Expand Up @@ -134,7 +144,7 @@ func TestSetupKeysHandlers(t *testing.T) {
[]byte(fmt.Sprintf("{\"name\":\"%s\",\"type\":\"%s\",\"expires_in\":86400, \"ephemeral\":true}", newSetupKey.Name, newSetupKey.Type))),
expectedStatus: http.StatusOK,
expectedBody: true,
expectedSetupKey: toResponseBody(newSetupKey),
expectedSetupKey: expectedNewKey,
},
{
name: "Update Setup Key",
Expand All @@ -150,6 +160,14 @@ func TestSetupKeysHandlers(t *testing.T) {
expectedBody: true,
expectedSetupKey: toResponseBody(updatedDefaultSetupKey),
},
{
name: "Delete Setup Key",
requestType: http.MethodDelete,
requestPath: "/api/setup-keys/" + defaultSetupKey.Id,
requestBody: bytes.NewBuffer([]byte("")),
expectedStatus: http.StatusOK,
expectedBody: false,
},
}

handler := initSetupKeysTestMetaData(defaultSetupKey, newSetupKey, updatedDefaultSetupKey, adminUser)
Expand All @@ -164,6 +182,7 @@ func TestSetupKeysHandlers(t *testing.T) {
router.HandleFunc("/api/setup-keys", handler.CreateSetupKey).Methods("POST", "OPTIONS")
router.HandleFunc("/api/setup-keys/{keyId}", handler.GetSetupKey).Methods("GET", "OPTIONS")
router.HandleFunc("/api/setup-keys/{keyId}", handler.UpdateSetupKey).Methods("PUT", "OPTIONS")
router.HandleFunc("/api/setup-keys/{keyId}", handler.DeleteSetupKey).Methods("DELETE", "OPTIONS")
router.ServeHTTP(recorder, req)

res := recorder.Result()
Expand Down
2 changes: 1 addition & 1 deletion management/server/metrics/selfhosted.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ func (w *Worker) generateProperties(ctx context.Context) properties {
peersSSHEnabled++
}

if peer.SetupKey == "" {
if peer.UserID != "" {
userPeers++
}

Expand Down
Loading

0 comments on commit 10480eb

Please sign in to comment.