From 92d6be6e627a76a578501c4362fcd49dfe5eb7bb Mon Sep 17 00:00:00 2001 From: Yury Gargay Date: Fri, 2 Feb 2024 12:24:29 +0100 Subject: [PATCH] Remove rules API endpoint --- management/server/file_store_test.go | 13 - management/server/http/api/openapi.yml | 210 ------------ management/server/http/api/types.gen.go | 66 ---- management/server/http/handler.go | 10 - .../server/http/policies_handler_test.go | 19 -- management/server/http/rules_handler.go | 305 ------------------ management/server/http/rules_handler_test.go | 265 --------------- management/server/mock_server/account_mock.go | 27 -- management/server/policy.go | 24 +- management/server/rule.go | 100 ------ management/server/sqlite_store.go | 2 +- 11 files changed, 12 insertions(+), 1029 deletions(-) delete mode 100644 management/server/http/rules_handler.go delete mode 100644 management/server/http/rules_handler_test.go delete mode 100644 management/server/rule.go diff --git a/management/server/file_store_test.go b/management/server/file_store_test.go index f1609aebdb5..a76be049fe3 100644 --- a/management/server/file_store_test.go +++ b/management/server/file_store_test.go @@ -258,18 +258,6 @@ func TestStore(t *testing.T) { t.Errorf("failed to restore a FileStore file - missing Group all") } - if restoredAccount.Rules["all"] == nil { - t.Errorf("failed to restore a FileStore file - missing Rule all") - return - } - - if restoredAccount.Rules["dmz"] == nil { - t.Errorf("failed to restore a FileStore file - missing Rule dmz") - return - } - assert.Equal(t, account.Rules["all"], restoredAccount.Rules["all"], "failed to restore a FileStore file - missing Rule all") - assert.Equal(t, account.Rules["dmz"], restoredAccount.Rules["dmz"], "failed to restore a FileStore file - missing Rule dmz") - if len(restoredAccount.Policies) != 2 { t.Errorf("failed to restore a FileStore file - missing Policies") return @@ -411,7 +399,6 @@ func TestFileStore_GetAccount(t *testing.T) { assert.Len(t, account.Peers, len(expected.Peers)) assert.Len(t, account.Users, len(expected.Users)) assert.Len(t, account.SetupKeys, len(expected.SetupKeys)) - assert.Len(t, account.Rules, len(expected.Rules)) assert.Len(t, account.Routes, len(expected.Routes)) assert.Len(t, account.NameServerGroups, len(expected.NameServerGroups)) } diff --git a/management/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 3bcc3279a9c..0ebcb238f19 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -17,8 +17,6 @@ tags: description: Interact with and view information about setup keys. - name: Groups description: Interact with and view information about groups. - - name: Rules - description: Interact with and view information about rules. - name: Policies description: Interact with and view information about policies. - name: Routes @@ -596,73 +594,6 @@ components: $ref: '#/components/schemas/PeerMinimum' required: - peers - RuleMinimum: - type: object - properties: - name: - description: Rule name identifier - type: string - example: Default - description: - description: Rule friendly description - type: string - example: This is a default rule that allows connections between all the resources - disabled: - description: Rules status - type: boolean - example: false - flow: - description: Rule flow, currently, only "bidirect" for bi-directional traffic is accepted - type: string - example: bidirect - required: - - name - - description - - disabled - - flow - RuleRequest: - allOf: - - $ref: '#/components/schemas/RuleMinimum' - - type: object - properties: - sources: - type: array - description: List of source group IDs - items: - type: string - example: "ch8i4ug6lnn4g9hqv7m1" - destinations: - type: array - description: List of destination group IDs - items: - type: string - example: "ch8i4ug6lnn4g9hqv7m0" - Rule: - allOf: - - type: object - properties: - id: - description: Rule ID - type: string - example: ch8i4ug6lnn4g9hqv7mg - required: - - id - - $ref: '#/components/schemas/RuleMinimum' - - type: object - properties: - sources: - description: Rule source group IDs - type: array - items: - $ref: '#/components/schemas/GroupMinimum' - destinations: - description: Rule destination group IDs - type: array - items: - $ref: '#/components/schemas/GroupMinimum' - required: - - sources - - destinations PolicyRuleMinimum: type: object properties: @@ -1814,147 +1745,6 @@ paths: "$ref": "#/components/responses/forbidden" '500': "$ref": "#/components/responses/internal_error" - /api/rules: - get: - summary: List all Rules - description: Returns a list of all rules. This will be deprecated in favour of `/api/policies`. - tags: [ Rules ] - deprecated: true - security: - - BearerAuth: [ ] - - TokenAuth: [ ] - responses: - '200': - description: A JSON Array of Rules - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Rule' - '400': - "$ref": "#/components/responses/bad_request" - '401': - "$ref": "#/components/responses/requires_authentication" - '403': - "$ref": "#/components/responses/forbidden" - '500': - "$ref": "#/components/responses/internal_error" - post: - summary: Create a Rule - description: Creates a rule. This will be deprecated in favour of `/api/policies`. - deprecated: true - tags: [ Rules ] - security: - - BearerAuth: [ ] - - TokenAuth: [ ] - requestBody: - description: New Rule request - content: - 'application/json': - schema: - $ref: '#/components/schemas/RuleRequest' - responses: - '200': - description: A Rule Object - content: - application/json: - schema: - $ref: '#/components/schemas/Rule' - /api/rules/{ruleId}: - get: - summary: Retrieve a Rule - description: Get information about a rules. This will be deprecated in favour of `/api/policies/{policyID}`. - deprecated: true - tags: [ Rules ] - security: - - BearerAuth: [ ] - - TokenAuth: [ ] - parameters: - - in: path - name: ruleId - required: true - schema: - type: string - description: The unique identifier of a rule - responses: - '200': - description: A Rule object - content: - application/json: - schema: - $ref: '#/components/schemas/Rule' - '400': - "$ref": "#/components/responses/bad_request" - '401': - "$ref": "#/components/responses/requires_authentication" - '403': - "$ref": "#/components/responses/forbidden" - '500': - "$ref": "#/components/responses/internal_error" - put: - summary: Update a Rule - description: Update/Replace a rule. This will be deprecated in favour of `/api/policies/{policyID}`. - deprecated: true - tags: [ Rules ] - security: - - BearerAuth: [ ] - - TokenAuth: [ ] - parameters: - - in: path - name: ruleId - required: true - schema: - type: string - description: The unique identifier of a rule - requestBody: - description: Update Rule request - content: - 'application/json': - schema: - $ref: '#/components/schemas/RuleRequest' - responses: - '200': - description: A Rule object - content: - application/json: - schema: - $ref: '#/components/schemas/Rule' - '400': - "$ref": "#/components/responses/bad_request" - '401': - "$ref": "#/components/responses/requires_authentication" - '403': - "$ref": "#/components/responses/forbidden" - '500': - "$ref": "#/components/responses/internal_error" - delete: - summary: Delete a Rule - description: Delete a rule. This will be deprecated in favour of `/api/policies/{policyID}`. - deprecated: true - tags: [ Rules ] - security: - - BearerAuth: [ ] - - TokenAuth: [ ] - parameters: - - in: path - name: ruleId - required: true - schema: - type: string - description: The unique identifier of a rule - 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/policies: get: summary: List all Policies diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index fe71c30db8a..cee95e31110 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -781,66 +781,6 @@ type RouteRequest struct { PeerGroups *[]string `json:"peer_groups,omitempty"` } -// Rule defines model for Rule. -type Rule struct { - // Description Rule friendly description - Description string `json:"description"` - - // Destinations Rule destination group IDs - Destinations []GroupMinimum `json:"destinations"` - - // Disabled Rules status - Disabled bool `json:"disabled"` - - // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted - Flow string `json:"flow"` - - // Id Rule ID - Id string `json:"id"` - - // Name Rule name identifier - Name string `json:"name"` - - // Sources Rule source group IDs - Sources []GroupMinimum `json:"sources"` -} - -// RuleMinimum defines model for RuleMinimum. -type RuleMinimum struct { - // Description Rule friendly description - Description string `json:"description"` - - // Disabled Rules status - Disabled bool `json:"disabled"` - - // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted - Flow string `json:"flow"` - - // Name Rule name identifier - Name string `json:"name"` -} - -// RuleRequest defines model for RuleRequest. -type RuleRequest struct { - // Description Rule friendly description - Description string `json:"description"` - - // Destinations List of destination group IDs - Destinations *[]string `json:"destinations,omitempty"` - - // Disabled Rules status - Disabled bool `json:"disabled"` - - // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted - Flow string `json:"flow"` - - // Name Rule name identifier - Name string `json:"name"` - - // Sources List of source group IDs - Sources *[]string `json:"sources,omitempty"` -} - // SetupKey defines model for SetupKey. type SetupKey struct { // AutoGroups List of group IDs to auto-assign to peers registered with this key @@ -1018,12 +958,6 @@ type PostApiRoutesJSONRequestBody = RouteRequest // PutApiRoutesRouteIdJSONRequestBody defines body for PutApiRoutesRouteId for application/json ContentType. type PutApiRoutesRouteIdJSONRequestBody = RouteRequest -// PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType. -type PostApiRulesJSONRequestBody = RuleRequest - -// PutApiRulesRuleIdJSONRequestBody defines body for PutApiRulesRuleId for application/json ContentType. -type PutApiRulesRuleIdJSONRequestBody = RuleRequest - // PostApiSetupKeysJSONRequestBody defines body for PostApiSetupKeys for application/json ContentType. type PostApiSetupKeysJSONRequestBody = SetupKeyRequest diff --git a/management/server/http/handler.go b/management/server/http/handler.go index c47eac5731f..2edcec21c1f 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -74,7 +74,6 @@ func APIHandler(accountManager s.AccountManager, jwtValidator jwtclaims.JWTValid api.addUsersEndpoint() api.addUsersTokensEndpoint() api.addSetupKeysEndpoint() - api.addRulesEndpoint() api.addPoliciesEndpoint() api.addGroupsEndpoint() api.addRoutesEndpoint() @@ -145,15 +144,6 @@ func (apiHandler *apiHandler) addSetupKeysEndpoint() { apiHandler.Router.HandleFunc("/setup-keys/{keyId}", keysHandler.UpdateSetupKey).Methods("PUT", "OPTIONS") } -func (apiHandler *apiHandler) addRulesEndpoint() { - rulesHandler := NewRulesHandler(apiHandler.AccountManager, apiHandler.AuthCfg) - apiHandler.Router.HandleFunc("/rules", rulesHandler.GetAllRules).Methods("GET", "OPTIONS") - apiHandler.Router.HandleFunc("/rules", rulesHandler.CreateRule).Methods("POST", "OPTIONS") - apiHandler.Router.HandleFunc("/rules/{ruleId}", rulesHandler.UpdateRule).Methods("PUT", "OPTIONS") - apiHandler.Router.HandleFunc("/rules/{ruleId}", rulesHandler.GetRule).Methods("GET", "OPTIONS") - apiHandler.Router.HandleFunc("/rules/{ruleId}", rulesHandler.DeleteRule).Methods("DELETE", "OPTIONS") -} - func (apiHandler *apiHandler) addPoliciesEndpoint() { policiesHandler := NewPoliciesHandler(apiHandler.AccountManager, apiHandler.AuthCfg) apiHandler.Router.HandleFunc("/policies", policiesHandler.GetAllPolicies).Methods("GET", "OPTIONS") diff --git a/management/server/http/policies_handler_test.go b/management/server/http/policies_handler_test.go index 86665848b53..e6b858036b7 100644 --- a/management/server/http/policies_handler_test.go +++ b/management/server/http/policies_handler_test.go @@ -3,7 +3,6 @@ package http import ( "bytes" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" @@ -44,24 +43,6 @@ func initPoliciesTestData(policies ...*server.Policy) *Policies { } return nil }, - SaveRuleFunc: func(_, _ string, rule *server.Rule) error { - if !strings.HasPrefix(rule.ID, "id-") { - rule.ID = "id-was-set" - } - return nil - }, - GetRuleFunc: func(_, ruleID, _ string) (*server.Rule, error) { - if ruleID != "idoftherule" { - return nil, fmt.Errorf("not found") - } - return &server.Rule{ - ID: "idoftherule", - Name: "Rule", - Source: []string{"idofsrcrule"}, - Destination: []string{"idofdestrule"}, - Flow: server.TrafficFlowBidirect, - }, nil - }, GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) { user := server.NewAdminUser("test_user") return &server.Account{ diff --git a/management/server/http/rules_handler.go b/management/server/http/rules_handler.go deleted file mode 100644 index bd501acf998..00000000000 --- a/management/server/http/rules_handler.go +++ /dev/null @@ -1,305 +0,0 @@ -package http - -import ( - "encoding/json" - "net/http" - - "github.com/gorilla/mux" - "github.com/rs/xid" - - "github.com/netbirdio/netbird/management/server" - "github.com/netbirdio/netbird/management/server/http/api" - "github.com/netbirdio/netbird/management/server/http/util" - "github.com/netbirdio/netbird/management/server/jwtclaims" - "github.com/netbirdio/netbird/management/server/status" -) - -// RulesHandler is a handler that returns rules of the account -type RulesHandler struct { - accountManager server.AccountManager - claimsExtractor *jwtclaims.ClaimsExtractor -} - -// NewRulesHandler creates a new RulesHandler HTTP handler -func NewRulesHandler(accountManager server.AccountManager, authCfg AuthCfg) *RulesHandler { - return &RulesHandler{ - accountManager: accountManager, - claimsExtractor: jwtclaims.NewClaimsExtractor( - jwtclaims.WithAudience(authCfg.Audience), - jwtclaims.WithUserIDClaim(authCfg.UserIDClaim), - ), - } -} - -// GetAllRules list for the account -func (h *RulesHandler) GetAllRules(w http.ResponseWriter, r *http.Request) { - claims := h.claimsExtractor.FromRequestContext(r) - account, user, err := h.accountManager.GetAccountFromToken(claims) - if err != nil { - util.WriteError(err, w) - return - } - - accountPolicies, err := h.accountManager.ListPolicies(account.Id, user.Id) - if err != nil { - util.WriteError(err, w) - return - } - rules := []*api.Rule{} - for _, policy := range accountPolicies { - for _, r := range policy.Rules { - rules = append(rules, toRuleResponse(account, r.ToRule())) - } - } - - util.WriteJSONObject(w, rules) -} - -// UpdateRule handles update to a rule identified by a given ID -func (h *RulesHandler) UpdateRule(w http.ResponseWriter, r *http.Request) { - claims := h.claimsExtractor.FromRequestContext(r) - account, user, err := h.accountManager.GetAccountFromToken(claims) - if err != nil { - util.WriteError(err, w) - return - } - - vars := mux.Vars(r) - ruleID := vars["ruleId"] - if len(ruleID) == 0 { - util.WriteError(status.Errorf(status.InvalidArgument, "invalid rule ID"), w) - return - } - - policy, err := h.accountManager.GetPolicy(account.Id, ruleID, user.Id) - if err != nil { - util.WriteError(err, w) - return - } - - var req api.PutApiRulesRuleIdJSONRequestBody - err = json.NewDecoder(r.Body).Decode(&req) - if err != nil { - util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) - } - - if req.Name == "" { - util.WriteError(status.Errorf(status.InvalidArgument, "rule name shouldn't be empty"), w) - return - } - - var reqSources []string - if req.Sources != nil { - reqSources = *req.Sources - } - - var reqDestinations []string - if req.Destinations != nil { - reqDestinations = *req.Destinations - } - - if len(policy.Rules) != 1 { - util.WriteError(status.Errorf(status.Internal, "policy should contain exactly one rule"), w) - return - } - - policy.Name = req.Name - policy.Description = req.Description - policy.Enabled = !req.Disabled - policy.Rules[0].ID = ruleID - policy.Rules[0].Name = req.Name - policy.Rules[0].Sources = reqSources - policy.Rules[0].Destinations = reqDestinations - policy.Rules[0].Enabled = !req.Disabled - policy.Rules[0].Description = req.Description - - switch req.Flow { - case server.TrafficFlowBidirectString: - policy.Rules[0].Action = server.PolicyTrafficActionAccept - default: - util.WriteError(status.Errorf(status.InvalidArgument, "unknown flow type"), w) - return - } - - err = h.accountManager.SavePolicy(account.Id, user.Id, policy) - if err != nil { - util.WriteError(err, w) - return - } - - resp := toRuleResponse(account, policy.Rules[0].ToRule()) - - util.WriteJSONObject(w, &resp) -} - -// CreateRule handles rule creation request -func (h *RulesHandler) CreateRule(w http.ResponseWriter, r *http.Request) { - claims := h.claimsExtractor.FromRequestContext(r) - account, user, err := h.accountManager.GetAccountFromToken(claims) - if err != nil { - util.WriteError(err, w) - return - } - - var req api.PostApiRulesJSONRequestBody - err = json.NewDecoder(r.Body).Decode(&req) - if err != nil { - util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) - return - } - - if req.Name == "" { - util.WriteError(status.Errorf(status.InvalidArgument, "rule name shouldn't be empty"), w) - return - } - - var reqSources []string - if req.Sources != nil { - reqSources = *req.Sources - } - - var reqDestinations []string - if req.Destinations != nil { - reqDestinations = *req.Destinations - } - - rule := server.Rule{ - ID: xid.New().String(), - Name: req.Name, - Source: reqSources, - Destination: reqDestinations, - Disabled: req.Disabled, - Description: req.Description, - } - - switch req.Flow { - case server.TrafficFlowBidirectString: - rule.Flow = server.TrafficFlowBidirect - default: - util.WriteError(status.Errorf(status.InvalidArgument, "unknown flow type"), w) - return - } - - policy, err := server.RuleToPolicy(&rule) - if err != nil { - util.WriteError(err, w) - return - } - err = h.accountManager.SavePolicy(account.Id, user.Id, policy) - if err != nil { - util.WriteError(err, w) - return - } - - resp := toRuleResponse(account, &rule) - - util.WriteJSONObject(w, &resp) -} - -// DeleteRule handles rule deletion request -func (h *RulesHandler) DeleteRule(w http.ResponseWriter, r *http.Request) { - claims := h.claimsExtractor.FromRequestContext(r) - account, user, err := h.accountManager.GetAccountFromToken(claims) - if err != nil { - util.WriteError(err, w) - return - } - aID := account.Id - - rID := mux.Vars(r)["ruleId"] - if len(rID) == 0 { - util.WriteError(status.Errorf(status.InvalidArgument, "invalid rule ID"), w) - return - } - - err = h.accountManager.DeletePolicy(aID, rID, user.Id) - if err != nil { - util.WriteError(err, w) - return - } - - util.WriteJSONObject(w, emptyObject{}) -} - -// GetRule handles a group Get request identified by ID -func (h *RulesHandler) GetRule(w http.ResponseWriter, r *http.Request) { - claims := h.claimsExtractor.FromRequestContext(r) - account, user, err := h.accountManager.GetAccountFromToken(claims) - if err != nil { - util.WriteError(err, w) - return - } - - switch r.Method { - case http.MethodGet: - ruleID := mux.Vars(r)["ruleId"] - if len(ruleID) == 0 { - util.WriteError(status.Errorf(status.InvalidArgument, "invalid rule ID"), w) - return - } - - policy, err := h.accountManager.GetPolicy(account.Id, ruleID, user.Id) - if err != nil { - util.WriteError(err, w) - return - } - - util.WriteJSONObject(w, toRuleResponse(account, policy.Rules[0].ToRule())) - default: - util.WriteError(status.Errorf(status.NotFound, "method not found"), w) - } -} - -func toRuleResponse(account *server.Account, rule *server.Rule) *api.Rule { - cache := make(map[string]api.GroupMinimum) - gr := api.Rule{ - Id: rule.ID, - Name: rule.Name, - Description: rule.Description, - Disabled: rule.Disabled, - } - - switch rule.Flow { - case server.TrafficFlowBidirect: - gr.Flow = server.TrafficFlowBidirectString - default: - gr.Flow = "unknown" - } - - for _, gid := range rule.Source { - _, ok := cache[gid] - if ok { - continue - } - - if group, ok := account.Groups[gid]; ok { - minimum := api.GroupMinimum{ - Id: group.ID, - Name: group.Name, - PeersCount: len(group.Peers), - } - - gr.Sources = append(gr.Sources, minimum) - cache[gid] = minimum - } - } - - for _, gid := range rule.Destination { - cachedMinimum, ok := cache[gid] - if ok { - gr.Destinations = append(gr.Destinations, cachedMinimum) - continue - } - if group, ok := account.Groups[gid]; ok { - minimum := api.GroupMinimum{ - Id: group.ID, - Name: group.Name, - PeersCount: len(group.Peers), - } - gr.Destinations = append(gr.Destinations, minimum) - cache[gid] = minimum - } - } - - return &gr -} diff --git a/management/server/http/rules_handler_test.go b/management/server/http/rules_handler_test.go deleted file mode 100644 index 27a308a0a56..00000000000 --- a/management/server/http/rules_handler_test.go +++ /dev/null @@ -1,265 +0,0 @@ -package http - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/netbirdio/netbird/management/server/http/api" - "github.com/netbirdio/netbird/management/server/status" - - "github.com/gorilla/mux" - - "github.com/netbirdio/netbird/management/server/jwtclaims" - - "github.com/magiconair/properties/assert" - - "github.com/netbirdio/netbird/management/server" - "github.com/netbirdio/netbird/management/server/mock_server" -) - -func initRulesTestData(rules ...*server.Rule) *RulesHandler { - testPolicies := make(map[string]*server.Policy, len(rules)) - for _, rule := range rules { - policy, err := server.RuleToPolicy(rule) - if err != nil { - panic(err) - } - testPolicies[policy.ID] = policy - } - return &RulesHandler{ - accountManager: &mock_server.MockAccountManager{ - GetPolicyFunc: func(_, policyID, _ string) (*server.Policy, error) { - policy, ok := testPolicies[policyID] - if !ok { - return nil, status.Errorf(status.NotFound, "policy not found") - } - return policy, nil - }, - SavePolicyFunc: func(_, _ string, policy *server.Policy) error { - if !strings.HasPrefix(policy.ID, "id-") { - policy.ID = "id-was-set" - } - return nil - }, - SaveRuleFunc: func(_, _ string, rule *server.Rule) error { - if !strings.HasPrefix(rule.ID, "id-") { - rule.ID = "id-was-set" - } - return nil - }, - GetRuleFunc: func(_, ruleID, _ string) (*server.Rule, error) { - if ruleID != "idoftherule" { - return nil, fmt.Errorf("not found") - } - return &server.Rule{ - ID: "idoftherule", - Name: "Rule", - Source: []string{"idofsrcrule"}, - Destination: []string{"idofdestrule"}, - Flow: server.TrafficFlowBidirect, - }, nil - }, - GetAccountFromTokenFunc: func(claims jwtclaims.AuthorizationClaims) (*server.Account, *server.User, error) { - user := server.NewAdminUser("test_user") - return &server.Account{ - Id: claims.AccountId, - Domain: "hotmail.com", - Rules: map[string]*server.Rule{"id-existed": {ID: "id-existed"}}, - Groups: map[string]*server.Group{ - "F": {ID: "F"}, - "G": {ID: "G"}, - }, - Users: map[string]*server.User{ - "test_user": user, - }, - }, user, nil - }, - }, - claimsExtractor: jwtclaims.NewClaimsExtractor( - jwtclaims.WithFromRequestContext(func(r *http.Request) jwtclaims.AuthorizationClaims { - return jwtclaims.AuthorizationClaims{ - UserId: "test_user", - Domain: "hotmail.com", - AccountId: "test_id", - } - }), - ), - } -} - -func TestRulesGetRule(t *testing.T) { - tt := []struct { - name string - expectedStatus int - expectedBody bool - requestType string - requestPath string - requestBody io.Reader - }{ - { - name: "GetRule OK", - expectedBody: true, - requestType: http.MethodGet, - requestPath: "/api/rules/idoftherule", - expectedStatus: http.StatusOK, - }, - { - name: "GetRule not found", - requestType: http.MethodGet, - requestPath: "/api/rules/notexists", - expectedStatus: http.StatusNotFound, - }, - } - - rule := &server.Rule{ - ID: "idoftherule", - Name: "Rule", - } - - p := initRulesTestData(rule) - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - recorder := httptest.NewRecorder() - req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody) - - router := mux.NewRouter() - router.HandleFunc("/api/rules/{ruleId}", p.GetRule).Methods("GET") - router.ServeHTTP(recorder, req) - - res := recorder.Result() - defer res.Body.Close() - - if status := recorder.Code; status != tc.expectedStatus { - t.Errorf("handler returned wrong status code: got %v want %v", - status, tc.expectedStatus) - return - } - - if !tc.expectedBody { - return - } - - content, err := io.ReadAll(res.Body) - if err != nil { - t.Fatalf("I don't know what I expected; %v", err) - } - - var got api.Rule - if err = json.Unmarshal(content, &got); err != nil { - t.Fatalf("Sent content is not in correct json format; %v", err) - } - - assert.Equal(t, got.Id, rule.ID) - assert.Equal(t, got.Name, rule.Name) - }) - } -} - -func TestRulesWriteRule(t *testing.T) { - tt := []struct { - name string - expectedStatus int - expectedBody bool - expectedRule *api.Rule - requestType string - requestPath string - requestBody io.Reader - }{ - { - name: "WriteRule POST OK", - requestType: http.MethodPost, - requestPath: "/api/rules", - requestBody: bytes.NewBuffer( - []byte(`{"Name":"Default POSTed Rule","Flow":"bidirect"}`)), - expectedStatus: http.StatusOK, - expectedBody: true, - expectedRule: &api.Rule{ - Id: "id-was-set", - Name: "Default POSTed Rule", - Flow: server.TrafficFlowBidirectString, - }, - }, - { - name: "WriteRule POST Invalid Name", - requestType: http.MethodPost, - requestPath: "/api/rules", - requestBody: bytes.NewBuffer( - []byte(`{"Name":"","Flow":"bidirect"}`)), - expectedStatus: http.StatusUnprocessableEntity, - expectedBody: false, - }, - { - name: "WriteRule PUT OK", - requestType: http.MethodPut, - requestPath: "/api/rules/id-existed", - requestBody: bytes.NewBuffer( - []byte(`{"Name":"Default POSTed Rule","Flow":"bidirect"}`)), - expectedStatus: http.StatusOK, - expectedBody: true, - expectedRule: &api.Rule{ - Id: "id-existed", - Name: "Default POSTed Rule", - Flow: server.TrafficFlowBidirectString, - }, - }, - { - name: "WriteRule PUT Invalid Name", - requestType: http.MethodPut, - requestPath: "/api/rules/id-existed", - requestBody: bytes.NewBuffer( - []byte(`{"Name":"","Flow":"bidirect"}`)), - expectedStatus: http.StatusUnprocessableEntity, - }, - } - - p := initRulesTestData(&server.Rule{ - ID: "id-existed", - Name: "Default POSTed Rule", - Flow: server.TrafficFlowBidirect, - }) - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - recorder := httptest.NewRecorder() - req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody) - - router := mux.NewRouter() - router.HandleFunc("/api/rules", p.CreateRule).Methods("POST") - router.HandleFunc("/api/rules/{ruleId}", p.UpdateRule).Methods("PUT") - router.ServeHTTP(recorder, req) - - res := recorder.Result() - defer res.Body.Close() - - content, err := io.ReadAll(res.Body) - if err != nil { - t.Fatalf("I don't know what I expected; %v", err) - } - - if status := recorder.Code; status != tc.expectedStatus { - t.Errorf("handler returned wrong status code: got %v want %v, content: %s", - status, tc.expectedStatus, string(content)) - return - } - - if !tc.expectedBody { - return - } - - got := &api.Rule{} - if err = json.Unmarshal(content, &got); err != nil { - t.Fatalf("Sent content is not in correct json format; %v", err) - } - tc.expectedRule.Id = got.Id - - assert.Equal(t, got, tc.expectedRule) - }) - } -} diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index c8e49457b2a..67eb78ce9c2 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -35,10 +35,7 @@ type MockAccountManager struct { ListGroupsFunc func(accountID string) ([]*server.Group, error) GroupAddPeerFunc func(accountID, groupID, peerID string) error GroupDeletePeerFunc func(accountID, groupID, peerID string) error - GetRuleFunc func(accountID, ruleID, userID string) (*server.Rule, error) - SaveRuleFunc func(accountID, userID string, rule *server.Rule) error DeleteRuleFunc func(accountID, ruleID, userID string) error - ListRulesFunc func(accountID, userID string) ([]*server.Rule, error) GetPolicyFunc func(accountID, policyID, userID string) (*server.Policy, error) SavePolicyFunc func(accountID, userID string, policy *server.Policy) error DeletePolicyFunc func(accountID, policyID, userID string) error @@ -294,22 +291,6 @@ func (am *MockAccountManager) GroupDeletePeer(accountID, groupID, peerID string) return status.Errorf(codes.Unimplemented, "method GroupDeletePeer is not implemented") } -// GetRule mock implementation of GetRule from server.AccountManager interface -func (am *MockAccountManager) GetRule(accountID, ruleID, userID string) (*server.Rule, error) { - if am.GetRuleFunc != nil { - return am.GetRuleFunc(accountID, ruleID, userID) - } - return nil, status.Errorf(codes.Unimplemented, "method GetRule is not implemented") -} - -// SaveRule mock implementation of SaveRule from server.AccountManager interface -func (am *MockAccountManager) SaveRule(accountID, userID string, rule *server.Rule) error { - if am.SaveRuleFunc != nil { - return am.SaveRuleFunc(accountID, userID, rule) - } - return status.Errorf(codes.Unimplemented, "method SaveRule is not implemented") -} - // DeleteRule mock implementation of DeleteRule from server.AccountManager interface func (am *MockAccountManager) DeleteRule(accountID, ruleID, userID string) error { if am.DeleteRuleFunc != nil { @@ -318,14 +299,6 @@ func (am *MockAccountManager) DeleteRule(accountID, ruleID, userID string) error return status.Errorf(codes.Unimplemented, "method DeleteRule is not implemented") } -// ListRules mock implementation of ListRules from server.AccountManager interface -func (am *MockAccountManager) ListRules(accountID, userID string) ([]*server.Rule, error) { - if am.ListRulesFunc != nil { - return am.ListRulesFunc(accountID, userID) - } - return nil, status.Errorf(codes.Unimplemented, "method ListRules is not implemented") -} - // GetPolicy mock implementation of GetPolicy from server.AccountManager interface func (am *MockAccountManager) GetPolicy(accountID, policyID, userID string) (*server.Policy, error) { if am.GetPolicyFunc != nil { diff --git a/management/server/policy.go b/management/server/policy.go index ff8b1dd9249..0fec1b14d06 100644 --- a/management/server/policy.go +++ b/management/server/policy.go @@ -51,6 +51,17 @@ const ( PolicyRuleFlowBidirect = PolicyRuleDirection("bidirect") ) +const ( + // DefaultRuleName is a name for the Default rule that is created for every account + DefaultRuleName = "Default" + // DefaultRuleDescription is a description for the Default rule that is created for every account + DefaultRuleDescription = "This is a default rule that allows connections between all the resources" + // DefaultPolicyName is a name for the Default policy that is created for every account + DefaultPolicyName = "Default" + // DefaultPolicyDescription is a description for the Default policy that is created for every account + DefaultPolicyDescription = "This is a default policy that allows connections between all the resources" +) + const ( firewallRuleDirectionIN = 0 firewallRuleDirectionOUT = 1 @@ -118,19 +129,6 @@ func (pm *PolicyRule) Copy() *PolicyRule { return rule } -// ToRule converts the PolicyRule to a legacy representation of the Rule (for backwards compatibility) -func (pm *PolicyRule) ToRule() *Rule { - return &Rule{ - ID: pm.ID, - Name: pm.Name, - Description: pm.Description, - Disabled: !pm.Enabled, - Flow: TrafficFlowBidirect, - Destination: pm.Destinations, - Source: pm.Sources, - } -} - // Policy of the Rego query type Policy struct { // ID of the policy' diff --git a/management/server/rule.go b/management/server/rule.go deleted file mode 100644 index 19085840cc7..00000000000 --- a/management/server/rule.go +++ /dev/null @@ -1,100 +0,0 @@ -package server - -import "fmt" - -// TrafficFlowType defines allowed direction of the traffic in the rule -type TrafficFlowType int - -const ( - // TrafficFlowBidirect allows traffic to both direction - TrafficFlowBidirect TrafficFlowType = iota - // TrafficFlowBidirectString allows traffic to both direction - TrafficFlowBidirectString = "bidirect" - // DefaultRuleName is a name for the Default rule that is created for every account - DefaultRuleName = "Default" - // DefaultRuleDescription is a description for the Default rule that is created for every account - DefaultRuleDescription = "This is a default rule that allows connections between all the resources" - // DefaultPolicyName is a name for the Default policy that is created for every account - DefaultPolicyName = "Default" - // DefaultPolicyDescription is a description for the Default policy that is created for every account - DefaultPolicyDescription = "This is a default policy that allows connections between all the resources" -) - -// Rule of ACL for groups -type Rule struct { - // ID of the rule - ID string - - // AccountID is a reference to Account that this object belongs - AccountID string `json:"-" gorm:"index"` - - // Name of the rule visible in the UI - Name string - - // Description of the rule visible in the UI - Description string - - // Disabled status of rule in the system - Disabled bool - - // Source list of groups IDs of peers - Source []string `gorm:"serializer:json"` - - // Destination list of groups IDs of peers - Destination []string `gorm:"serializer:json"` - - // Flow of the traffic allowed by the rule - Flow TrafficFlowType -} - -func (r *Rule) Copy() *Rule { - rule := &Rule{ - ID: r.ID, - Name: r.Name, - Description: r.Description, - Disabled: r.Disabled, - Source: make([]string, len(r.Source)), - Destination: make([]string, len(r.Destination)), - Flow: r.Flow, - } - copy(rule.Source, r.Source) - copy(rule.Destination, r.Destination) - return rule -} - -// EventMeta returns activity event meta related to this rule -func (r *Rule) EventMeta() map[string]any { - return map[string]any{"name": r.Name} -} - -// ToPolicyRule converts a Rule to a PolicyRule object -func (r *Rule) ToPolicyRule() *PolicyRule { - if r == nil { - return nil - } - return &PolicyRule{ - ID: r.ID, - Name: r.Name, - Enabled: !r.Disabled, - Description: r.Description, - Destinations: r.Destination, - Sources: r.Source, - Bidirectional: true, - Protocol: PolicyRuleProtocolALL, - Action: PolicyTrafficActionAccept, - } -} - -// RuleToPolicy converts a Rule to a Policy query object -func RuleToPolicy(rule *Rule) (*Policy, error) { - if rule == nil { - return nil, fmt.Errorf("rule is empty") - } - return &Policy{ - ID: rule.ID, - Name: rule.Name, - Description: rule.Description, - Enabled: !rule.Disabled, - Rules: []*PolicyRule{rule.ToPolicyRule()}, - }, nil -} diff --git a/management/server/sqlite_store.go b/management/server/sqlite_store.go index c8d31a0efca..aed003214f8 100644 --- a/management/server/sqlite_store.go +++ b/management/server/sqlite_store.go @@ -61,7 +61,7 @@ func NewSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*SqliteStore, sql.SetMaxOpenConns(conns) // TODO: make it configurable err = db.AutoMigrate( - &SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{}, + &SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{}, &installation{}, &account.ExtraSettings{}, )