From 0c6c5fdc7008d0bd56803ec48e15761971db21bf Mon Sep 17 00:00:00 2001 From: Pascal Fischer Date: Mon, 18 Mar 2024 15:31:47 +0100 Subject: [PATCH] extend example --- management/refactor/api/http/handler.go | 122 + management/refactor/api/http/specs/api.yaml | 0 management/refactor/api/http/specs/cfg.yaml | 7 + .../refactor/api/http/specs/generate.sh | 16 + .../refactor/api/http/specs/openapi.yaml | 2870 +++++++++++++++++ .../refactor/api/http/specs/types.gen.go | 1 + management/refactor/mesh/controller.go | 63 +- management/refactor/mesh/network.go | 15 - management/refactor/peers/manager.go | 50 - management/refactor/peers/repository.go | 8 - management/refactor/policies/policy.go | 7 - management/refactor/resources/dns/api.go | 1 + management/refactor/resources/dns/manager.go | 1 + .../refactor/resources/dns/repository.go | 1 + .../refactor/resources/dns/types/config.go | 11 + .../resources/dns/types/custom_zone.go | 9 + .../resources/dns/types/name_server.go | 75 + .../resources/dns/types/name_server_group.go | 65 + .../refactor/resources/dns/types/settings.go | 7 + .../resources/dns/types/simpe_record.go | 53 + management/refactor/resources/groups/api.go | 1 + .../refactor/resources/groups/manager.go | 1 + .../refactor/resources/groups/repository.go | 1 + .../refactor/resources/groups/types/group.go | 23 + management/refactor/resources/network/api.go | 1 + .../refactor/resources/network/manager.go | 19 + .../refactor/resources/network/repository.go | 1 + .../resources/network/types/network.go | 70 + .../resources/network/types/network_map.go | 18 + management/refactor/resources/peers/api.go | 317 ++ .../refactor/resources/peers/manager.go | 51 + .../refactor/resources/peers/repository.go | 10 + .../{peers => resources/peers/types}/peer.go | 19 +- .../resources/peers/types/peer_status.go | 24 + .../resources/peers/types/peer_system_meta.go | 69 + management/refactor/resources/policies/api.go | 1 + .../{ => resources}/policies/manager.go | 13 +- .../resources/policies/posture/api.go | 1 + .../resources/policies/posture/manager.go | 1 + .../resources/policies/posture/repository.go | 1 + .../{ => resources}/policies/repository.go | 0 .../resources/policies/types/firewall_rule.go | 19 + .../resources/policies/types/policy.go | 13 + .../resources/policies/types/policy_rule.go | 13 + management/refactor/resources/routes/api.go | 1 + .../refactor/resources/routes/manager.go | 100 + .../refactor/resources/routes/repository.go | 4 + .../refactor/resources/routes/types/route.go | 54 + management/refactor/resources/settings/api.go | 1 + .../refactor/resources/settings/manager.go | 21 + .../refactor/resources/settings/repository.go | 7 + .../settings/types}/settings.go | 2 +- .../refactor/resources/setup_keys/api.go | 1 + .../refactor/resources/setup_keys/manager.go | 1 + .../resources/setup_keys/repository.go | 1 + .../resources/setup_keys/types/setup_key.go | 7 + management/refactor/resources/users/api.go | 1 + .../refactor/resources/users/manager.go | 27 + .../users/personal_access_tokens/api.go | 1 + .../users/personal_access_tokens/manager.go | 1 + .../personal_access_tokens/repository.go | 1 + .../types/personal_access_token.go | 1 + .../{ => resources}/users/repository.go | 0 .../{users => resources/users/types}/user.go | 2 +- management/refactor/settings/manager.go | 19 - management/refactor/settings/repository.go | 5 - management/refactor/store/account.go | 50 + management/refactor/store/postgres_store.go | 14 +- management/refactor/store/sqlite_store.go | 257 +- management/refactor/store/store.go | 25 +- management/refactor/users/manager.go | 24 - 71 files changed, 4481 insertions(+), 216 deletions(-) create mode 100644 management/refactor/api/http/handler.go delete mode 100644 management/refactor/api/http/specs/api.yaml create mode 100644 management/refactor/api/http/specs/cfg.yaml create mode 100755 management/refactor/api/http/specs/generate.sh create mode 100644 management/refactor/api/http/specs/openapi.yaml create mode 100644 management/refactor/api/http/specs/types.gen.go delete mode 100644 management/refactor/mesh/network.go delete mode 100644 management/refactor/peers/manager.go delete mode 100644 management/refactor/peers/repository.go delete mode 100644 management/refactor/policies/policy.go create mode 100644 management/refactor/resources/dns/api.go create mode 100644 management/refactor/resources/dns/manager.go create mode 100644 management/refactor/resources/dns/repository.go create mode 100644 management/refactor/resources/dns/types/config.go create mode 100644 management/refactor/resources/dns/types/custom_zone.go create mode 100644 management/refactor/resources/dns/types/name_server.go create mode 100644 management/refactor/resources/dns/types/name_server_group.go create mode 100644 management/refactor/resources/dns/types/settings.go create mode 100644 management/refactor/resources/dns/types/simpe_record.go create mode 100644 management/refactor/resources/groups/api.go create mode 100644 management/refactor/resources/groups/manager.go create mode 100644 management/refactor/resources/groups/repository.go create mode 100644 management/refactor/resources/groups/types/group.go create mode 100644 management/refactor/resources/network/api.go create mode 100644 management/refactor/resources/network/manager.go create mode 100644 management/refactor/resources/network/repository.go create mode 100644 management/refactor/resources/network/types/network.go create mode 100644 management/refactor/resources/network/types/network_map.go create mode 100644 management/refactor/resources/peers/api.go create mode 100644 management/refactor/resources/peers/manager.go create mode 100644 management/refactor/resources/peers/repository.go rename management/refactor/{peers => resources/peers/types}/peer.go (90%) create mode 100644 management/refactor/resources/peers/types/peer_status.go create mode 100644 management/refactor/resources/peers/types/peer_system_meta.go create mode 100644 management/refactor/resources/policies/api.go rename management/refactor/{ => resources}/policies/manager.go (57%) create mode 100644 management/refactor/resources/policies/posture/api.go create mode 100644 management/refactor/resources/policies/posture/manager.go create mode 100644 management/refactor/resources/policies/posture/repository.go rename management/refactor/{ => resources}/policies/repository.go (100%) create mode 100644 management/refactor/resources/policies/types/firewall_rule.go create mode 100644 management/refactor/resources/policies/types/policy.go create mode 100644 management/refactor/resources/policies/types/policy_rule.go create mode 100644 management/refactor/resources/routes/api.go create mode 100644 management/refactor/resources/routes/manager.go create mode 100644 management/refactor/resources/routes/repository.go create mode 100644 management/refactor/resources/routes/types/route.go create mode 100644 management/refactor/resources/settings/api.go create mode 100644 management/refactor/resources/settings/manager.go create mode 100644 management/refactor/resources/settings/repository.go rename management/refactor/{settings => resources/settings/types}/settings.go (97%) create mode 100644 management/refactor/resources/setup_keys/api.go create mode 100644 management/refactor/resources/setup_keys/manager.go create mode 100644 management/refactor/resources/setup_keys/repository.go create mode 100644 management/refactor/resources/setup_keys/types/setup_key.go create mode 100644 management/refactor/resources/users/api.go create mode 100644 management/refactor/resources/users/manager.go create mode 100644 management/refactor/resources/users/personal_access_tokens/api.go create mode 100644 management/refactor/resources/users/personal_access_tokens/manager.go create mode 100644 management/refactor/resources/users/personal_access_tokens/repository.go create mode 100644 management/refactor/resources/users/personal_access_tokens/types/personal_access_token.go rename management/refactor/{ => resources}/users/repository.go (100%) rename management/refactor/{users => resources/users/types}/user.go (98%) delete mode 100644 management/refactor/settings/manager.go delete mode 100644 management/refactor/settings/repository.go create mode 100644 management/refactor/store/account.go delete mode 100644 management/refactor/users/manager.go diff --git a/management/refactor/api/http/handler.go b/management/refactor/api/http/handler.go new file mode 100644 index 000000000..b80c0658a --- /dev/null +++ b/management/refactor/api/http/handler.go @@ -0,0 +1,122 @@ +package http + +import ( + "context" + "fmt" + "net/http" + + "github.com/gorilla/mux" + "github.com/rs/cors" + + "github.com/netbirdio/management-integrations/integrations" + "github.com/netbirdio/netbird/management/refactor/resources/peers" + s "github.com/netbirdio/netbird/management/server" + "github.com/netbirdio/netbird/management/server/geolocation" + + "github.com/netbirdio/netbird/management/server/http/middleware" + "github.com/netbirdio/netbird/management/server/jwtclaims" + "github.com/netbirdio/netbird/management/server/telemetry" +) + +const apiPrefix = "/api" + +// AuthCfg contains parameters for authentication middleware +type AuthCfg struct { + Issuer string + Audience string + UserIDClaim string + KeysLocation string +} + +type DefaultAPIHandler struct { + Router *mux.Router + AccountManager s.AccountManager + geolocationManager *geolocation.Geolocation + AuthCfg AuthCfg +} + +// EmptyObject is an empty struct used to return empty JSON object +type EmptyObject struct { +} + +// NewDefaultAPIHandler creates the Management service HTTP API handler registering all the available endpoints. +func NewDefaultAPIHandler(ctx context.Context, jwtValidator jwtclaims.JWTValidator, appMetrics telemetry.AppMetrics, authCfg AuthCfg) (http.Handler, error) { + claimsExtractor := jwtclaims.NewClaimsExtractor( + jwtclaims.WithAudience(authCfg.Audience), + jwtclaims.WithUserIDClaim(authCfg.UserIDClaim), + ) + + authMiddleware := middleware.NewAuthMiddleware( + accountManager.GetAccountFromPAT, + jwtValidator.ValidateAndParse, + accountManager.MarkPATUsed, + accountManager.CheckUserAccessByJWTGroups, + claimsExtractor, + authCfg.Audience, + authCfg.UserIDClaim, + ) + + corsMiddleware := cors.AllowAll() + + acMiddleware := middleware.NewAccessControl( + authCfg.Audience, + authCfg.UserIDClaim, + accountManager.GetUser) + + rootRouter := mux.NewRouter() + metricsMiddleware := appMetrics.HTTPMiddleware() + + prefix := apiPrefix + router := rootRouter.PathPrefix(prefix).Subrouter() + router.Use(metricsMiddleware.Handler, corsMiddleware.Handler, authMiddleware.Handler, acMiddleware.Handler) + + api := DefaultAPIHandler{ + Router: router, + AccountManager: accountManager, + geolocationManager: LocationManager, + AuthCfg: authCfg, + } + + if _, err := integrations.RegisterHandlers(ctx, prefix, api.Router, accountManager, claimsExtractor); err != nil { + return nil, fmt.Errorf("register integrations endpoints: %w", err) + } + + peers.RegisterPeersEndpoints(api.Router) + // api.addAccountsEndpoint() + // api.addPeersEndpoint() + // api.addUsersEndpoint() + // api.addUsersTokensEndpoint() + // api.addSetupKeysEndpoint() + // api.addRulesEndpoint() + // api.addPoliciesEndpoint() + // api.addGroupsEndpoint() + // api.addRoutesEndpoint() + // api.addDNSNameserversEndpoint() + // api.addDNSSettingEndpoint() + // api.addEventsEndpoint() + // api.addPostureCheckEndpoint() + // api.addLocationsEndpoint() + + err := api.Router.Walk(func(route *mux.Route, _ *mux.Router, _ []*mux.Route) error { + methods, err := route.GetMethods() + if err != nil { // we may have wildcard routes from integrations without methods, skip them for now + methods = []string{} + } + for _, method := range methods { + template, err := route.GetPathTemplate() + if err != nil { + return err + } + err = metricsMiddleware.AddHTTPRequestResponseCounter(template, method) + if err != nil { + return err + } + } + return nil + }) + if err != nil { + return nil, err + } + + return rootRouter, nil +} diff --git a/management/refactor/api/http/specs/api.yaml b/management/refactor/api/http/specs/api.yaml deleted file mode 100644 index e69de29bb..000000000 diff --git a/management/refactor/api/http/specs/cfg.yaml b/management/refactor/api/http/specs/cfg.yaml new file mode 100644 index 000000000..b0ccd8321 --- /dev/null +++ b/management/refactor/api/http/specs/cfg.yaml @@ -0,0 +1,7 @@ +package: api +generate: + models: true + embedded-spec: false +output: types.gen.go +compatibility: + always-prefix-enum-values: true \ No newline at end of file diff --git a/management/refactor/api/http/specs/generate.sh b/management/refactor/api/http/specs/generate.sh new file mode 100755 index 000000000..2f24fd903 --- /dev/null +++ b/management/refactor/api/http/specs/generate.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +if ! which realpath > /dev/null 2>&1 +then + echo realpath is not installed + echo run: brew install coreutils + exit 1 +fi + +old_pwd=$(pwd) +script_path=$(dirname $(realpath "$0")) +cd "$script_path" +go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@4a1477f6a8ba6ca8115cc23bb2fb67f0b9fca18e +oapi-codegen --config cfg.yaml openapi.yml +cd "$old_pwd" \ No newline at end of file diff --git a/management/refactor/api/http/specs/openapi.yaml b/management/refactor/api/http/specs/openapi.yaml new file mode 100644 index 000000000..b2ddfd5cc --- /dev/null +++ b/management/refactor/api/http/specs/openapi.yaml @@ -0,0 +1,2870 @@ +openapi: 3.1.0 +servers: + - url: https://api.netbird.io + description: Default server +info: + title: NetBird REST API + description: API to manipulate groups, rules, policies and retrieve information about peers and users + version: 0.0.1 +tags: + - name: Users + description: Interact with and view information about users. + - name: Tokens + description: Interact with and view information about tokens. + - name: Peers + description: Interact with and view information about peers. + - name: Setup Keys + 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: Posture Checks + description: Interact with and view information about posture checks. + - name: Routes + description: Interact with and view information about routes. + - name: DNS + description: Interact with and view information about DNS configuration. + - name: Events + description: View information about the account and network events. + - name: Accounts + description: View information about the accounts. +components: + schemas: + Account: + type: object + properties: + id: + description: Account ID + type: string + example: ch8i4ug6lnn4g9hqv7l0 + settings: + $ref: '#/components/schemas/AccountSettings' + required: + - id + - settings + AccountSettings: + type: object + properties: + peer_login_expiration_enabled: + description: Enables or disables peer login expiration globally. After peer's login has expired the user has to log in (authenticate). Applies only to peers that were added by a user (interactive SSO login). + type: boolean + example: true + peer_login_expiration: + description: Period of time after which peer login expires (seconds). + type: integer + example: 43200 + groups_propagation_enabled: + description: Allows propagate the new user auto groups to peers that belongs to the user + type: boolean + example: true + jwt_groups_enabled: + description: Allows extract groups from JWT claim and add it to account groups. + type: boolean + example: true + jwt_groups_claim_name: + description: Name of the claim from which we extract groups names to add it to account groups. + type: string + example: "roles" + jwt_allow_groups: + description: List of groups to which users are allowed access + type: array + items: + type: string + example: Administrators + extra: + $ref: '#/components/schemas/AccountExtraSettings' + required: + - peer_login_expiration_enabled + - peer_login_expiration + AccountExtraSettings: + type: object + properties: + peer_approval_enabled: + description: (Cloud only) Enables or disables peer approval globally. If enabled, all peers added will be in pending state until approved by an admin. + type: boolean + example: true + AccountRequest: + type: object + properties: + settings: + $ref: '#/components/schemas/AccountSettings' + required: + - settings + User: + type: object + properties: + id: + description: User ID + type: string + example: google-oauth2|277474792786460067937 + email: + description: User's email address + type: string + example: demo@netbird.io + name: + description: User's name from idp provider + type: string + example: Tom Schulz + role: + description: User's NetBird account role + type: string + example: admin + status: + description: User's status + type: string + enum: [ "active","invited","blocked" ] + example: active + last_login: + description: Last time this user performed a login to the dashboard + type: string + format: date-time + example: "2023-05-05T09:00:35.477782Z" + auto_groups: + description: Group IDs to auto-assign to peers registered by this user + type: array + items: + type: string + example: ch8i4ug6lnn4g9hqv7m0 + is_current: + description: Is true if authenticated user is the same as this user + type: boolean + readOnly: true + example: true + is_service_user: + description: Is true if this user is a service user + type: boolean + readOnly: true + example: false + is_blocked: + description: Is true if this user is blocked. Blocked users can't use the system + type: boolean + example: false + issued: + description: How user was issued by API or Integration + type: string + example: api + required: + - id + - email + - name + - role + - auto_groups + - status + - is_blocked + UserRequest: + type: object + properties: + role: + description: User's NetBird account role + type: string + example: admin + auto_groups: + description: Group IDs to auto-assign to peers registered by this user + type: array + items: + type: string + example: ch8i4ug6lnn4g9hqv7m0 + is_blocked: + description: If set to true then user is blocked and can't use the system + type: boolean + example: false + required: + - role + - auto_groups + - is_blocked + UserCreateRequest: + type: object + properties: + email: + description: User's Email to send invite to + type: string + example: demo@netbird.io + name: + description: User's full name + type: string + example: Tom Schulz + role: + description: User's NetBird account role + type: string + example: admin + auto_groups: + description: Group IDs to auto-assign to peers registered by this user + type: array + items: + type: string + example: ch8i4ug6lnn4g9hqv7m0 + is_service_user: + description: Is true if this user is a service user + type: boolean + example: false + required: + - role + - auto_groups + - is_service_user + PeerMinimum: + type: object + properties: + id: + description: Peer ID + type: string + example: chacbco6lnnbn6cg5s90 + name: + description: Peer's hostname + type: string + example: stage-host-1 + required: + - id + - name + PeerRequest: + type: object + properties: + name: + type: string + example: stage-host-1 + ssh_enabled: + type: boolean + example: true + login_expiration_enabled: + type: boolean + example: false + approval_required: + description: (Cloud only) Indicates whether peer needs approval + type: boolean + example: true + required: + - name + - ssh_enabled + - login_expiration_enabled + PeerBase: + allOf: + - $ref: '#/components/schemas/PeerMinimum' + - type: object + properties: + ip: + description: Peer's IP address + type: string + example: 10.64.0.1 + connection_ip: + description: Peer's public connection IP address + type: string + example: 35.64.0.1 + connected: + description: Peer to Management connection status + type: boolean + example: true + last_seen: + description: Last time peer connected to Netbird's management service + type: string + format: date-time + example: "2023-05-05T10:05:26.420578Z" + os: + description: Peer's operating system and version + type: string + example: Darwin 13.2.1 + kernel_version: + description: Peer's operating system kernel version + type: string + example: 23.2.0 + geoname_id: + description: Unique identifier from the GeoNames database for a specific geographical location. + type: integer + example: 2643743 + version: + description: Peer's daemon or cli version + type: string + example: 0.14.0 + groups: + description: Groups that the peer belongs to + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + ssh_enabled: + description: Indicates whether SSH server is enabled on this peer + type: boolean + example: true + user_id: + description: User ID of the user that enrolled this peer + type: string + example: google-oauth2|277474792786460067937 + hostname: + description: Hostname of the machine + type: string + example: stage-host-1 + ui_version: + description: Peer's desktop UI version + type: string + example: 0.14.0 + dns_label: + description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud + type: string + example: stage-host-1.netbird.cloud + login_expiration_enabled: + description: Indicates whether peer login expiration has been enabled or not + type: boolean + example: false + login_expired: + description: Indicates whether peer's login expired or not + type: boolean + example: false + last_login: + description: Last time this peer performed log in (authentication). E.g., user authenticated. + type: string + format: date-time + example: "2023-05-05T09:00:35.477782Z" + approval_required: + description: (Cloud only) Indicates whether peer needs approval + type: boolean + example: true + country_code: + $ref: '#/components/schemas/CountryCode' + city_name: + $ref: '#/components/schemas/CityName' + required: + - city_name + - connected + - connection_ip + - country_code + - dns_label + - geoname_id + - groups + - hostname + - ip + - kernel_version + - last_login + - last_seen + - login_expiration_enabled + - login_expired + - os + - ssh_enabled + - user_id + - version + - ui_version + AccessiblePeer: + allOf: + - $ref: '#/components/schemas/PeerMinimum' + - type: object + properties: + ip: + description: Peer's IP address + type: string + example: 10.64.0.1 + dns_label: + description: Peer's DNS label is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's domain to the peer label. e.g. peer-dns-label.netbird.cloud + type: string + example: stage-host-1.netbird.cloud + user_id: + description: User ID of the user that enrolled this peer + type: string + example: google-oauth2|277474792786460067937 + required: + - ip + - dns_label + - user_id + Peer: + allOf: + - $ref: '#/components/schemas/PeerBase' + - type: object + properties: + accessible_peers: + description: List of accessible peers + type: array + items: + $ref: '#/components/schemas/AccessiblePeer' + required: + - accessible_peers + PeerBatch: + allOf: + - $ref: '#/components/schemas/PeerBase' + - type: object + properties: + accessible_peers_count: + description: Number of accessible peers + type: integer + example: 5 + required: + - accessible_peers_count + SetupKey: + type: object + properties: + id: + description: Setup Key ID + type: string + example: 2531583362 + key: + description: Setup Key value + type: string + example: A616097E-FCF0-48FA-9354-CA4A61142761 + name: + description: Setup key name identifier + type: string + example: Default key + expires: + description: Setup Key expiration date + type: string + format: date-time + example: "2023-06-01T14:47:22.291057Z" + type: + description: Setup key type, one-off for single time usage and reusable + type: string + example: reusable + valid: + description: Setup key validity status + type: boolean + example: true + revoked: + description: Setup key revocation status + type: boolean + example: false + used_times: + description: Usage count of setup key + type: integer + example: 2 + last_used: + description: Setup key last usage date + type: string + format: date-time + example: "2023-05-05T09:00:35.477782Z" + state: + description: Setup key status, "valid", "overused","expired" or "revoked" + type: string + example: valid + auto_groups: + description: List of group IDs to auto-assign to peers registered with this key + type: array + items: + type: string + example: "ch8i4ug6lnn4g9hqv7m0" + updated_at: + description: Setup key last update date + type: string + format: date-time + example: "2023-05-05T09:00:35.477782Z" + usage_limit: + description: A number of times this key can be used. The value of 0 indicates the unlimited usage. + type: integer + example: 0 + ephemeral: + description: Indicate that the peer will be ephemeral or not + type: boolean + example: true + required: + - id + - key + - name + - expires + - type + - valid + - revoked + - used_times + - last_used + - state + - auto_groups + - updated_at + - usage_limit + - ephemeral + SetupKeyRequest: + type: object + properties: + name: + description: Setup Key name + type: string + example: Default key + type: + description: Setup key type, one-off for single time usage and reusable + type: string + example: reusable + expires_in: + description: Expiration time in seconds + type: integer + minimum: 86400 + maximum: 31536000 + example: 86400 + revoked: + description: Setup key revocation status + type: boolean + example: false + auto_groups: + description: List of group IDs to auto-assign to peers registered with this key + type: array + items: + type: string + example: "ch8i4ug6lnn4g9hqv7m0" + usage_limit: + description: A number of times this key can be used. The value of 0 indicates the unlimited usage. + type: integer + example: 0 + ephemeral: + description: Indicate that the peer will be ephemeral or not + type: boolean + example: true + required: + - name + - type + - expires_in + - revoked + - auto_groups + - usage_limit + PersonalAccessToken: + type: object + properties: + id: + description: ID of a token + type: string + example: ch8i54g6lnn4g9hqv7n0 + name: + description: Name of the token + type: string + example: My first token + expiration_date: + description: Date the token expires + type: string + format: date-time + example: "2023-05-05T14:38:28.977616Z" + created_by: + description: User ID of the user who created the token + type: string + example: google-oauth2|277474792786460067937 + created_at: + description: Date the token was created + type: string + format: date-time + example: "2023-05-02T14:48:20.465209Z" + last_used: + description: Date the token was last used + type: string + format: date-time + example: "2023-05-04T12:45:25.9723616Z" + required: + - id + - name + - expiration_date + - created_by + - created_at + PersonalAccessTokenGenerated: + type: object + properties: + plain_token: + description: Plain text representation of the generated token + type: string + example: 2023-05-02T14:48:20.465209Z + personal_access_token: + $ref: '#/components/schemas/PersonalAccessToken' + required: + - plain_token + - personal_access_token + PersonalAccessTokenRequest: + type: object + properties: + name: + description: Name of the token + type: string + example: My first token + expires_in: + description: Expiration in days + type: integer + minimum: 1 + maximum: 365 + example: 30 + required: + - name + - expires_in + GroupMinimum: + type: object + properties: + id: + description: Group ID + type: string + example: ch8i4ug6lnn4g9hqv7m0 + name: + description: Group Name identifier + type: string + example: devs + peers_count: + description: Count of peers associated to the group + type: integer + example: 2 + issued: + description: How group was issued by API or from JWT token + type: string + example: api + required: + - id + - name + - peers_count + GroupRequest: + type: object + properties: + name: + type: string + description: Group name identifier + example: devs + peers: + type: array + description: List of peers ids + items: + type: string + example: "ch8i4ug6lnn4g9hqv7m1" + required: + - name + Group: + allOf: + - $ref: '#/components/schemas/GroupMinimum' + - type: object + properties: + peers: + description: List of peers object + type: array + items: + $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: + id: + description: Policy rule ID + type: string + example: ch8i4ug6lnn4g9hqv7mg + name: + description: Policy rule name identifier + type: string + example: Default + description: + description: Policy rule friendly description + type: string + example: This is a default rule that allows connections between all the resources + enabled: + description: Policy rule status + type: boolean + example: true + action: + description: Policy rule accept or drops packets + type: string + enum: ["accept","drop"] + example: "accept" + bidirectional: + description: Define if the rule is applicable in both directions, sources, and destinations. + type: boolean + example: true + protocol: + description: Policy rule type of the traffic + type: string + enum: ["all", "tcp", "udp", "icmp"] + example: "tcp" + ports: + description: Policy rule affected ports or it ranges list + type: array + items: + type: string + example: "80" + required: + - name + - enabled + - bidirectional + - protocol + - action + PolicyRuleUpdate: + allOf: + - $ref: '#/components/schemas/PolicyRuleMinimum' + - type: object + properties: + sources: + description: Policy rule source group IDs + type: array + items: + type: string + example: "ch8i4ug6lnn4g9hqv797" + destinations: + description: Policy rule destination group IDs + type: array + items: + type: string + example: "ch8i4ug6lnn4g9h7v7m0" + required: + - sources + - destinations + PolicyRule: + allOf: + - $ref: '#/components/schemas/PolicyRuleMinimum' + - type: object + properties: + sources: + description: Policy rule source group IDs + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + destinations: + description: Policy rule destination group IDs + type: array + items: + $ref: '#/components/schemas/GroupMinimum' + required: + - sources + - destinations + PolicyMinimum: + type: object + properties: + id: + description: Policy ID + type: string + example: ch8i4ug6lnn4g9hqv7mg + name: + description: Policy name identifier + type: string + example: ch8i4ug6lnn4g9hqv7mg + description: + description: Policy friendly description + type: string + example: This is a default policy that allows connections between all the resources + enabled: + description: Policy status + type: boolean + example: true + required: + - name + - description + - enabled + PolicyUpdate: + allOf: + - $ref: '#/components/schemas/PolicyMinimum' + - type: object + properties: + source_posture_checks: + description: Posture checks ID's applied to policy source groups + type: array + items: + type: string + example: "chacdk86lnnboviihd70" + rules: + description: Policy rule object for policy UI editor + type: array + items: + $ref: '#/components/schemas/PolicyRuleUpdate' + required: + - rules + Policy: + allOf: + - $ref: '#/components/schemas/PolicyMinimum' + - type: object + properties: + source_posture_checks: + description: Posture checks ID's applied to policy source groups + type: array + items: + type: string + example: "chacdk86lnnboviihd70" + rules: + description: Policy rule object for policy UI editor + type: array + items: + $ref: '#/components/schemas/PolicyRule' + required: + - rules + - source_posture_checks + PostureCheck: + type: object + properties: + id: + description: Posture check ID + type: string + example: ch8i4ug6lnn4g9hqv7mg + name: + description: Posture check unique name identifier + type: string + example: Default + description: + description: Posture check friendly description + type: string + example: This checks if the peer is running required NetBird's version + checks: + $ref: '#/components/schemas/Checks' + required: + - id + - name + - checks + Checks: + description: List of objects that perform the actual checks + type: object + properties: + nb_version_check: + $ref: '#/components/schemas/NBVersionCheck' + os_version_check: + $ref: '#/components/schemas/OSVersionCheck' + geo_location_check: + $ref: '#/components/schemas/GeoLocationCheck' + peer_network_range_check: + $ref: '#/components/schemas/PeerNetworkRangeCheck' + NBVersionCheck: + description: Posture check for the version of NetBird + type: object + $ref: '#/components/schemas/MinVersionCheck' + OSVersionCheck: + description: Posture check for the version of operating system + type: object + properties: + android: + description: Minimum version of Android + $ref: '#/components/schemas/MinVersionCheck' + darwin: + $ref: '#/components/schemas/MinVersionCheck' + ios: + description: Minimum version of iOS + $ref: '#/components/schemas/MinVersionCheck' + linux: + description: Minimum Linux kernel version + $ref: '#/components/schemas/MinKernelVersionCheck' + windows: + description: Minimum Windows kernel build version + $ref: '#/components/schemas/MinKernelVersionCheck' + example: + android: + min_version: "13" + ios: + min_version: "17.3.1" + darwin: + min_version: "14.2.1" + linux: + min_kernel_version: "5.3.3" + windows: + min_kernel_version: "10.0.1234" + MinVersionCheck: + description: Posture check for the version of operating system + type: object + properties: + min_version: + description: Minimum acceptable version + type: string + example: "14.3" + required: + - min_version + MinKernelVersionCheck: + description: Posture check with the kernel version + type: object + properties: + min_kernel_version: + description: Minimum acceptable version + type: string + example: "6.6.12" + required: + - min_kernel_version + GeoLocationCheck: + description: Posture check for geo location + type: object + properties: + locations: + description: List of geo locations to which the policy applies + type: array + items: + $ref: '#/components/schemas/Location' + action: + description: Action to take upon policy match + type: string + enum: [ "allow", "deny" ] + example: "allow" + required: + - locations + - action + PeerNetworkRangeCheck: + description: Posture check for allow or deny access based on peer local network addresses + type: object + properties: + ranges: + description: List of peer network ranges in CIDR notation + type: array + items: + type: string + example: ["192.168.1.0/24", "10.0.0.0/8", "2001:db8:1234:1a00::/56"] + action: + description: Action to take upon policy match + type: string + enum: [ "allow", "deny" ] + example: "allow" + required: + - ranges + - action + Location: + description: Describe geographical location information + type: object + properties: + country_code: + $ref: '#/components/schemas/CountryCode' + city_name: + $ref: '#/components/schemas/CityName' + required: + - country_code + CountryCode: + description: 2-letter ISO 3166-1 alpha-2 code that represents the country + type: string + example: "DE" + CityName: + description: Commonly used English name of the city + type: string + example: "Berlin" + Country: + description: Describe country geographical location information + type: object + properties: + country_name: + description: Commonly used English name of the country + type: string + example: "Germany" + country_code: + $ref: '#/components/schemas/CountryCode' + required: + - country_name + - country_code + City: + description: Describe city geographical location information + type: object + properties: + geoname_id: + description: Integer ID of the record in GeoNames database + type: integer + example: 2950158 + city_name: + description: Commonly used English name of the city + type: string + example: "Berlin" + required: + - geoname_id + - city_name + PostureCheckUpdate: + type: object + properties: + name: + description: Posture check name identifier + type: string + example: Default + description: + description: Posture check friendly description + type: string + example: This checks if the peer is running required NetBird's version + checks: + $ref: '#/components/schemas/Checks' + required: + - name + - description + RouteRequest: + type: object + properties: + description: + description: Route description + type: string + example: My first route + network_id: + description: Route network identifier, to group HA routes + type: string + maxLength: 40 + minLength: 1 + example: Route 1 + enabled: + description: Route status + type: boolean + example: true + peer: + description: Peer Identifier associated with route. This property can not be set together with `peer_groups` + type: string + example: chacbco6lnnbn6cg5s91 + peer_groups: + description: Peers Group Identifier associated with route. This property can not be set together with `peer` + type: array + items: + type: string + example: chacbco6lnnbn6cg5s91 + network: + description: Network range in CIDR format + type: string + example: 10.64.0.0/24 + metric: + description: Route metric number. Lowest number has higher priority + type: integer + maximum: 9999 + minimum: 1 + example: 9999 + masquerade: + description: Indicate if peer should masquerade traffic to this route's prefix + type: boolean + example: true + groups: + description: Group IDs containing routing peers + type: array + items: + type: string + example: "chacdk86lnnboviihd70" + required: + - id + - description + - network_id + - enabled + # Only one property has to be set + #- peer + #- peer_groups + - network + - metric + - masquerade + - groups + Route: + allOf: + - type: object + properties: + id: + description: Route Id + type: string + example: chacdk86lnnboviihd7g + network_type: + description: Network type indicating if it is IPv4 or IPv6 + type: string + example: IPv4 + required: + - id + - network_type + - $ref: '#/components/schemas/RouteRequest' + Nameserver: + type: object + properties: + ip: + description: Nameserver IP + type: string + example: 8.8.8.8 + ns_type: + description: Nameserver Type + type: string + enum: [ "udp" ] + example: udp + port: + description: Nameserver Port + type: integer + example: 53 + required: + - ip + - ns_type + - port + NameserverGroupRequest: + type: object + properties: + name: + description: Name of nameserver group name + type: string + maxLength: 40 + minLength: 1 + example: Google DNS + description: + description: Description of the nameserver group + type: string + example: Google DNS servers + nameservers: + description: Nameserver list + minLength: 1 + maxLength: 3 + type: array + items: + $ref: '#/components/schemas/Nameserver' + enabled: + description: Nameserver group status + type: boolean + example: true + groups: + description: Distribution group IDs that defines group of peers that will use this nameserver group + type: array + items: + type: string + example: ch8i4ug6lnn4g9hqv7m0 + primary: + description: Defines if a nameserver group is primary that resolves all domains. It should be true only if domains list is empty. + type: boolean + example: true + domains: + description: Match domain list. It should be empty only if primary is true. + type: array + items: + type: string + minLength: 1 + maxLength: 255 + example: "example.com" + search_domains_enabled: + description: Search domain status for match domains. It should be true only if domains list is not empty. + type: boolean + example: true + required: + - name + - description + - nameservers + - enabled + - groups + - primary + - domains + - search_domains_enabled + NameserverGroup: + allOf: + - type: object + properties: + id: + description: Nameserver group ID + type: string + example: ch8i4ug6lnn4g9hqv7m0 + required: + - id + - $ref: '#/components/schemas/NameserverGroupRequest' + DNSSettings: + type: object + properties: + disabled_management_groups: + description: Groups whose DNS management is disabled + type: array + items: + type: string + example: ch8i4ug6lnn4g9hqv7m0 + required: + - disabled_management_groups + Event: + type: object + properties: + id: + description: Event unique identifier + type: string + example: 10 + timestamp: + description: The date and time when the event occurred + type: string + format: date-time + example: "2023-05-05T10:04:37.473542Z" + activity: + description: The activity that occurred during the event + type: string + example: Route created + activity_code: + description: The string code of the activity that occurred during the event + type: string + enum: [ "user.peer.delete", "user.join", "user.invite", "user.peer.add", "user.group.add", "user.group.delete", + "user.role.update", "user.block", "user.unblock", "user.peer.login", + "setupkey.peer.add", "setupkey.add", "setupkey.update", "setupkey.revoke", "setupkey.overuse", + "setupkey.group.delete", "setupkey.group.add", + "rule.add", "rule.delete", "rule.update", + "policy.add", "policy.delete", "policy.update", + "group.add", "group.update", "dns.setting.disabled.management.group.add", "dns.setting.disabled.management.group.delete", + "account.create", "account.setting.peer.login.expiration.update", "account.setting.peer.login.expiration.disable", "account.setting.peer.login.expiration.enable", + "route.add", "route.delete", "route.update", + "nameserver.group.add", "nameserver.group.delete", "nameserver.group.update", + "peer.ssh.disable", "peer.ssh.enable", "peer.rename", "peer.login.expiration.disable", "peer.login.expiration.enable", "peer.login.expire", + "service.user.create", "personal.access.token.create", "service.user.delete", "personal.access.token.delete" ] + example: route.add + initiator_id: + description: The ID of the initiator of the event. E.g., an ID of a user that triggered the event. + type: string + example: google-oauth2|123456789012345678901 + initiator_name: + description: The name of the initiator of the event. + type: string + example: John Doe + initiator_email: + description: The e-mail address of the initiator of the event. E.g., an e-mail of a user that triggered the event. + type: string + example: demo@netbird.io + target_id: + description: The ID of the target of the event. E.g., an ID of the peer that a user removed. + type: string + example: chad9d86lnnc59g18ou0 + meta: + description: The metadata of the event + type: object + additionalProperties: + type: string + example: { "name": "my route", "network_range": "10.64.0.0/24", "peer_id": "chacbco6lnnbn6cg5s91"} + required: + - id + - timestamp + - activity + - activity_code + - initiator_id + - initiator_name + - initiator_email + - target_id + - meta + responses: + not_found: + description: Resource not found + content: { } + validation_failed_simple: + description: Validation failed + content: { } + bad_request: + description: Bad Request + content: { } + internal_error: + description: Internal Server Error + content: { } + validation_failed: + description: Validation failed + content: { } + forbidden: + description: Forbidden + content: { } + requires_authentication: + description: Requires authentication + content: { } + securitySchemes: + BearerAuth: + type: http + scheme: bearer + bearerFormat: JWT + TokenAuth: + type: apiKey + in: header + name: Authorization + description: >- + Enter the token with the `Token` prefix, e.g. "Token nbp_F3f0d.....". +security: + - BearerAuth: [ ] + - TokenAuth: [ ] +paths: + /api/accounts: + get: + summary: List all Accounts + description: Returns a list of accounts of a user. Always returns a list of one account. + tags: [ Accounts ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON array of accounts + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Account' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/accounts/{accountId}: + delete: + summary: Delete an Account + description: Deletes an account and all its resources. Only administrators and account owners can delete accounts. + tags: [ Accounts ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: accountId + required: true + schema: + type: string + description: The unique identifier of an account + responses: + '200': + description: Delete account 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" + put: + summary: Update an Account + description: Update information about an account + tags: [ Accounts ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: accountId + required: true + schema: + type: string + description: The unique identifier of an account + requestBody: + description: update an account + content: + 'application/json': + schema: + $ref: '#/components/schemas/AccountRequest' + responses: + '200': + description: An Account object + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users: + get: + summary: List all Users + description: Returns a list of all users + tags: [ Users ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: query + name: service_user + schema: + type: boolean + description: Filters users and returns either regular users or service users + responses: + '200': + description: A JSON array of Users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' + '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 User + description: Creates a new service user or sends an invite to a regular user + tags: [ Users ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: User invite information + content: + 'application/json': + schema: + $ref: '#/components/schemas/UserCreateRequest' + responses: + '200': + description: A User object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users/{userId}: + put: + summary: Update a User + description: Update information about a User + tags: [ Users ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + requestBody: + description: User update + content: + 'application/json': + schema: + $ref: '#/components/schemas/UserRequest' + responses: + '200': + description: A User object + content: + application/json: + schema: + $ref: '#/components/schemas/User' + '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 User + description: This method removes a user from accessing the system. For this leaves the IDP user intact unless the `--user-delete-from-idp` is passed to management startup. + tags: [ Users ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + 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/users/{userId}/tokens: + get: + summary: List all Tokens + description: Returns a list of all tokens for a user + tags: [ Tokens ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + responses: + '200': + description: A JSON Array of PersonalAccessTokens + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PersonalAccessToken' + '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 Token + description: Create a new token for a user + tags: [ Tokens ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + requestBody: + description: PersonalAccessToken create parameters + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessTokenRequest' + responses: + '200': + description: The token in plain text + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessTokenGenerated' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/users/{userId}/tokens/{tokenId}: + get: + summary: Retrieve a Token + description: Returns a specific token for a user + tags: [ Tokens ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + - in: path + name: tokenId + required: true + schema: + type: string + description: The unique identifier of a token + responses: + '200': + description: A PersonalAccessTokens Object + content: + application/json: + schema: + $ref: '#/components/schemas/PersonalAccessToken' + '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 Token + description: Delete a token for a user + tags: [ Tokens ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + - in: path + name: tokenId + required: true + schema: + type: string + description: The unique identifier of a token + 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/users/{userId}/invite: + post: + summary: Resend user invitation + description: Resend user invitation + tags: [ Users ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: userId + required: true + schema: + type: string + description: The unique identifier of a user + responses: + '200': + description: Invite 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/peers: + get: + summary: List all Peers + description: Returns a list of all peers + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Peers + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PeerBatch' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/peers/{peerId}: + get: + summary: Retrieve a Peer + description: Get information about a peer + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + responses: + '200': + description: A Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/Peer' + '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 Peer + description: Update information about a peer + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + requestBody: + description: update a peer + content: + 'application/json': + schema: + $ref: '#/components/schemas/PeerRequest' + responses: + '200': + description: A Peer object + content: + application/json: + schema: + $ref: '#/components/schemas/Peer' + '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 Peer + description: Delete a peer + tags: [ Peers ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: peerId + required: true + schema: + type: string + description: The unique identifier of a peer + 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/setup-keys: + get: + summary: List all Setup Keys + description: Returns a list of all Setup Keys + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Setup keys + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/SetupKey' + '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 Setup Key + description: Creates a setup key + tags: [ Setup Keys ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Setup Key request + content: + 'application/json': + schema: + $ref: '#/components/schemas/SetupKeyRequest' + responses: + '200': + description: A Setup Keys Object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/setup-keys/{keyId}: + get: + summary: Retrieve a Setup Key + description: Get information about 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: A Setup Key object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '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 Setup Key + description: Update information about 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 + requestBody: + description: update to Setup Key + content: + 'application/json': + schema: + $ref: '#/components/schemas/SetupKeyRequest' + responses: + '200': + description: A Setup Key object + content: + application/json: + schema: + $ref: '#/components/schemas/SetupKey' + '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 + description: Returns a list of all groups + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Groups + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Group' + '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 Group + description: Creates a group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Group request + content: + 'application/json': + schema: + $ref: '#/components/schemas/GroupRequest' + responses: + '200': + description: A Group Object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '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/{groupId}: + get: + summary: Retrieve a Group + description: Get information about a group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + responses: + '200': + description: A Group object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '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 Group + description: Update/Replace a group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + requestBody: + description: Update Group request + content: + 'application/json': + schema: + $ref: '#/components/schemas/GroupRequest' + responses: + '200': + description: A Group object + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + '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 Group + description: Delete a group + tags: [ Groups ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: groupId + required: true + schema: + type: string + description: The unique identifier of a group + 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/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 + description: Returns a list of all policies + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Policies + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Policy' + '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 Policy + description: Creates a policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Policy request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PolicyUpdate' + responses: + '200': + description: A Policy Object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + /api/policies/{policyId}: + get: + summary: Retrieve a Policy + description: Get information about a Policies + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + responses: + '200': + description: A Policy object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + '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 Policy + description: Update/Replace a Policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + requestBody: + description: Update Policy request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PolicyUpdate' + responses: + '200': + description: A Policy object + content: + application/json: + schema: + $ref: '#/components/schemas/Policy' + '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 Policy + description: Delete a policy + tags: [ Policies ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: policyId + required: true + schema: + type: string + description: The unique identifier of a policy + 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/routes: + get: + summary: List all Routes + description: Returns a list of all routes + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Routes + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Route' + '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 Route + description: Creates a Route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Routes request + content: + 'application/json': + schema: + $ref: '#/components/schemas/RouteRequest' + responses: + '200': + description: A Route Object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/routes/{routeId}: + get: + summary: Retrieve a Route + description: Get information about a Routes + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + responses: + '200': + description: A Route object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '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 Route + description: Update/Replace a Route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + requestBody: + description: Update Route request + content: + application/json: + schema: + $ref: '#/components/schemas/RouteRequest' + responses: + '200': + description: A Route object + content: + application/json: + schema: + $ref: '#/components/schemas/Route' + '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 Route + description: Delete a route + tags: [ Routes ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: routeId + required: true + schema: + type: string + description: The unique identifier of a route + 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/dns/nameservers: + get: + summary: List all Nameserver Groups + description: Returns a list of all Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Nameserver Groups + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/NameserverGroup' + '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 Nameserver Group + description: Creates a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New Nameserver Groups request + content: + 'application/json': + schema: + $ref: '#/components/schemas/NameserverGroupRequest' + responses: + '200': + description: A Nameserver Groups Object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/dns/nameservers/{nsgroupId}: + get: + summary: Retrieve a Nameserver Group + description: Get information about a Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + responses: + '200': + description: A Nameserver Group object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '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 Nameserver Group + description: Update/Replace a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + requestBody: + description: Update Nameserver Group request + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroupRequest' + responses: + '200': + description: A Nameserver Group object + content: + application/json: + schema: + $ref: '#/components/schemas/NameserverGroup' + '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 Nameserver Group + description: Delete a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: nsgroupId + required: true + schema: + type: string + description: The unique identifier of a Nameserver Group + 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/dns/settings: + get: + summary: Retrieve DNS settings + description: Returns a DNS settings object + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Object of DNS Setting + content: + application/json: + schema: + items: + $ref: '#/components/schemas/DNSSettings' + '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 DNS Settings + description: Updates a DNS settings object + tags: [ DNS ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: A DNS settings object + content: + 'application/json': + schema: + $ref: '#/components/schemas/DNSSettings' + responses: + '200': + description: A JSON Object of DNS Setting + content: + application/json: + schema: + $ref: '#/components/schemas/DNSSettings' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/events: + get: + summary: List all Events + description: Returns a list of all events + tags: [ Events ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of Events + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Event' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/posture-checks: + get: + summary: List all Posture Checks + description: Returns a list of all posture checks + tags: [ "Posture Checks" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: A JSON Array of posture checks + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PostureCheck' + '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 Posture Check + description: Creates a posture check + tags: [ "Posture Checks" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + requestBody: + description: New posture check request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PostureCheckUpdate' + responses: + '200': + description: A posture check Object + content: + application/json: + schema: + $ref: '#/components/schemas/PostureCheck' + /api/posture-checks/{postureCheckId}: + get: + summary: Retrieve a Posture Check + description: Get information about a posture check + tags: [ "Posture Checks" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: postureCheckId + required: true + schema: + type: string + description: The unique identifier of a posture check + responses: + '200': + description: A posture check object + content: + application/json: + schema: + $ref: '#/components/schemas/PostureCheck' + '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 Posture Check + description: Update/Replace a posture check + tags: [ "Posture Checks" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: postureCheckId + required: true + schema: + type: string + description: The unique identifier of a posture check + requestBody: + description: Update Rule request + content: + 'application/json': + schema: + $ref: '#/components/schemas/PostureCheckUpdate' + responses: + '200': + description: A posture check object + content: + application/json: + schema: + $ref: '#/components/schemas/PostureCheck' + '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 Posture Check + description: Delete a posture check + tags: [ "Posture Checks" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: postureCheckId + required: true + schema: + type: string + description: The unique identifier of a posture check + 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/locations/countries: + get: + summary: List all country codes + description: Get list of all country in 2-letter ISO 3166-1 alpha-2 codes + tags: [ "Geo Locations" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + responses: + '200': + description: List of country codes + content: + application/json: + schema: + type: array + items: + type: string + example: "DE" + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" + /api/locations/countries/{country}/cities: + get: + summary: List all city names by country + description: Get a list of all English city names for a given country code + tags: [ "Geo Locations" ] + security: + - BearerAuth: [ ] + - TokenAuth: [ ] + parameters: + - in: path + name: country + required: true + schema: + $ref: '#/components/schemas/Country' + responses: + '200': + description: List of city names + content: + application/json: + schema: + $ref: '#/components/schemas/City' + '400': + "$ref": "#/components/responses/bad_request" + '401': + "$ref": "#/components/responses/requires_authentication" + '403': + "$ref": "#/components/responses/forbidden" + '500': + "$ref": "#/components/responses/internal_error" \ No newline at end of file diff --git a/management/refactor/api/http/specs/types.gen.go b/management/refactor/api/http/specs/types.gen.go new file mode 100644 index 000000000..84b30ecb1 --- /dev/null +++ b/management/refactor/api/http/specs/types.gen.go @@ -0,0 +1 @@ +package specs diff --git a/management/refactor/mesh/controller.go b/management/refactor/mesh/controller.go index 5baf241a1..d979be2f8 100644 --- a/management/refactor/mesh/controller.go +++ b/management/refactor/mesh/controller.go @@ -3,11 +3,16 @@ package mesh import ( "github.com/netbirdio/management-integrations/integrations" nbdns "github.com/netbirdio/netbird/dns" - "github.com/netbirdio/netbird/management/refactor/peers" - "github.com/netbirdio/netbird/management/refactor/policies" - "github.com/netbirdio/netbird/management/refactor/settings" + "github.com/netbirdio/netbird/management/refactor/api/http" + "github.com/netbirdio/netbird/management/refactor/resources/network" + networkTypes "github.com/netbirdio/netbird/management/refactor/resources/network/types" + "github.com/netbirdio/netbird/management/refactor/resources/peers" + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + "github.com/netbirdio/netbird/management/refactor/resources/policies" + "github.com/netbirdio/netbird/management/refactor/resources/routes" + "github.com/netbirdio/netbird/management/refactor/resources/settings" + "github.com/netbirdio/netbird/management/refactor/resources/users" "github.com/netbirdio/netbird/management/refactor/store" - "github.com/netbirdio/netbird/management/refactor/users" "github.com/netbirdio/netbird/management/server/activity" "github.com/netbirdio/netbird/management/server/status" ) @@ -23,16 +28,22 @@ type DefaultController struct { userManager users.Manager policiesManager policies.Manager settingsManager settings.Manager + networkManager network.Manager + routesManager routes.Manager } func NewDefaultController() *DefaultController { storeStore, _ := store.NewDefaultStore(store.SqliteStoreEngine, "", nil) settingsManager := settings.NewDefaultManager(storeStore) + networkManager := network.NewDefaultManager() peersManager := peers.NewDefaultManager(storeStore, settingsManager) + routesManager := routes.NewDefaultManager(storeStore, peersManager) usersManager := users.NewDefaultManager(storeStore, peersManager) policiesManager := policies.NewDefaultManager(storeStore, peersManager) - peersManager, settingsManager, usersManager, policiesManager, storeStore = integrations.InjectCloud(peersManager, policiesManager, settingsManager, usersManager, storeStore) + apiHandler, _ := http.NewDefaultAPIHandler() + + peersManager, settingsManager, usersManager, policiesManager, storeStore, apiHandler = integrations.InjectCloud(peersManager, policiesManager, settingsManager, usersManager, storeStore) return &DefaultController{ store: storeStore, @@ -40,10 +51,12 @@ func NewDefaultController() *DefaultController { userManager: usersManager, policiesManager: policiesManager, settingsManager: settingsManager, + networkManager: networkManager, + routesManager: routesManager, } } -func (c *DefaultController) LoginPeer(login peers.PeerLogin) { +func (c *DefaultController) LoginPeer(login peerTypes.PeerLogin) (*peerTypes.Peer, *networkTypes.NetworkMap, error) { peer, err := c.peersManager.GetPeerByPubKey(login.WireGuardPubKey) if err != nil { @@ -60,7 +73,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) { } } - account, err := pm.accountManager.GetAccount(peer.GetAccountID()) + settings, err := c.settingsManager.GetSettings(peer.GetAccountID()) if err != nil { return nil, nil, err } @@ -68,7 +81,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) { // this flag prevents unnecessary calls to the persistent store. shouldStorePeer := false updateRemotePeers := false - if peerLoginExpired(peer, account) { + if peerLoginExpired(peer, settings) { err = checkAuth(login.UserID, peer) if err != nil { return nil, nil, err @@ -79,7 +92,7 @@ func (c *DefaultController) LoginPeer(login peers.PeerLogin) { updateRemotePeers = true shouldStorePeer = true - pm.eventsManager.StoreEvent(login.UserID, peer.ID, account.Id, activity.UserLoggedInPeer, peer.EventMeta(pm.accountManager.GetDNSDomain())) + pm.eventsManager.StoreEvent(login.UserID, peer.GetID(), peer.GetAccountID(), activity.UserLoggedInPeer, peer.EventMeta(pm.accountManager.GetDNSDomain())) } if peer.UpdateMetaIfNew(login.Meta) { @@ -107,29 +120,37 @@ func (c *DefaultController) SyncPeer() { } -func (c *DefaultController) GetPeerNetworkMap(peerID, dnsDomain string) *NetworkMap { +func (c *DefaultController) GetPeerNetworkMap(accountID, peerID, dnsDomain string) (*networkTypes.NetworkMap, error) { + unlock := c.store.AcquireAccountLock(accountID) + defer unlock() + + network, err := c.networkManager.GetNetwork(accountID) + if err != nil { + return nil, err + } + peer, err := c.peersManager.GetNetworkPeerByID(peerID) if err != nil { - return &NetworkMap{ - Network: a.Network.Copy(), - } + return &networkTypes.NetworkMap{ + Network: network.Copy(), + }, nil } aclPeers, firewallRules := c.policiesManager.GetAccessiblePeersAndFirewallRules(peerID) // exclude expired peers - var peersToConnect []peers.Peer - var expiredPeers []peers.Peer + var peersToConnect []*peerTypes.Peer + var expiredPeers []*peerTypes.Peer accSettings, _ := c.settingsManager.GetSettings(peer.GetAccountID()) for _, p := range aclPeers { expired, _ := p.LoginExpired(accSettings.GetPeerLoginExpiration()) if accSettings.GetPeerLoginExpirationEnabled() && expired { - expiredPeers = append(expiredPeers, p) + expiredPeers = append(expiredPeers, &p) continue } - peersToConnect = append(peersToConnect, p) + peersToConnect = append(peersToConnect, &p) } - routesUpdate := a.getRoutesToSync(peerID, peersToConnect) + routesUpdate := c.routesManager.GetRoutesToSync(peerID, peersToConnect, accountID) dnsManagementStatus := a.getPeerDNSManagementStatus(peerID) dnsUpdate := nbdns.Config{ @@ -146,12 +167,12 @@ func (c *DefaultController) GetPeerNetworkMap(peerID, dnsDomain string) *Network dnsUpdate.NameServerGroups = getPeerNSGroups(a, peerID) } - return &NetworkMap{ + return &networkTypes.NetworkMap{ Peers: peersToConnect, - Network: a.Network.Copy(), + Network: network.Copy(), Routes: routesUpdate, DNSConfig: dnsUpdate, OfflinePeers: expiredPeers, FirewallRules: firewallRules, - } + }, nil } diff --git a/management/refactor/mesh/network.go b/management/refactor/mesh/network.go deleted file mode 100644 index a7fe69806..000000000 --- a/management/refactor/mesh/network.go +++ /dev/null @@ -1,15 +0,0 @@ -package mesh - -import ( - nbdns "github.com/netbirdio/netbird/dns" - nbpeer "github.com/netbirdio/netbird/management/server/peer" -) - -type NetworkMap struct { - Peers []*nbpeer.Peer - Network *Network - Routes []*route.Route - DNSConfig nbdns.Config - OfflinePeers []*nbpeer.Peer - FirewallRules []*FirewallRule -} diff --git a/management/refactor/peers/manager.go b/management/refactor/peers/manager.go deleted file mode 100644 index 5094a5181..000000000 --- a/management/refactor/peers/manager.go +++ /dev/null @@ -1,50 +0,0 @@ -package peers - -import ( - "github.com/netbirdio/netbird/management/refactor/settings" -) - -type Manager interface { - GetPeerByPubKey(pubKey string) (Peer, error) - GetPeerByID(id string) (Peer, error) - GetNetworkPeerByID(id string) (Peer, error) - GetNetworkPeersInAccount(id string) ([]Peer, error) -} - -type DefaultManager struct { - repository Repository - settingsManager settings.Manager -} - -func NewDefaultManager(repository Repository, settingsManager settings.Manager) *DefaultManager { - return &DefaultManager{ - repository: repository, - settingsManager: settingsManager, - } -} - -func (dm *DefaultManager) GetNetworkPeerByID(id string) (Peer, error) { - return dm.repository.FindPeerByID(id) -} - -func (dm *DefaultManager) GetNetworkPeersInAccount(id string) ([]Peer, error) { - defaultPeers, err := dm.repository.FindAllPeersInAccount(id) - if err != nil { - return nil, err - } - - peers := make([]Peer, len(defaultPeers)) - for _, dp := range defaultPeers { - peers = append(peers, dp) - } - - return peers, nil -} - -func (dm *DefaultManager) GetPeerByPubKey(pubKey string) (Peer, error) { - return dm.repository.FindPeerByPubKey(pubKey) -} - -func (dm *DefaultManager) GetPeerByID(id string) (Peer, error) { - return dm.repository.FindPeerByID(id) -} diff --git a/management/refactor/peers/repository.go b/management/refactor/peers/repository.go deleted file mode 100644 index 15dc9bf77..000000000 --- a/management/refactor/peers/repository.go +++ /dev/null @@ -1,8 +0,0 @@ -package peers - -type Repository interface { - FindPeerByPubKey(pubKey string) (Peer, error) - FindPeerByID(id string) (Peer, error) - FindAllPeersInAccount(id string) ([]Peer, error) - UpdatePeer(peer Peer) error -} diff --git a/management/refactor/policies/policy.go b/management/refactor/policies/policy.go deleted file mode 100644 index e2dda9756..000000000 --- a/management/refactor/policies/policy.go +++ /dev/null @@ -1,7 +0,0 @@ -package policies - -type Policy interface { -} - -type DefaultPolicy struct { -} diff --git a/management/refactor/resources/dns/api.go b/management/refactor/resources/dns/api.go new file mode 100644 index 000000000..1ffe03d57 --- /dev/null +++ b/management/refactor/resources/dns/api.go @@ -0,0 +1 @@ +package dns diff --git a/management/refactor/resources/dns/manager.go b/management/refactor/resources/dns/manager.go new file mode 100644 index 000000000..1ffe03d57 --- /dev/null +++ b/management/refactor/resources/dns/manager.go @@ -0,0 +1 @@ +package dns diff --git a/management/refactor/resources/dns/repository.go b/management/refactor/resources/dns/repository.go new file mode 100644 index 000000000..1ffe03d57 --- /dev/null +++ b/management/refactor/resources/dns/repository.go @@ -0,0 +1 @@ +package dns diff --git a/management/refactor/resources/dns/types/config.go b/management/refactor/resources/dns/types/config.go new file mode 100644 index 000000000..867abf858 --- /dev/null +++ b/management/refactor/resources/dns/types/config.go @@ -0,0 +1,11 @@ +package types + +// Config represents a dns configuration that is exchanged between management and peers +type Config struct { + // ServiceEnable indicates if the service should be enabled + ServiceEnable bool + // NameServerGroups contains a list of nameserver group + NameServerGroups []*NameServerGroup + // CustomZones contains a list of custom zone + CustomZones []CustomZone +} diff --git a/management/refactor/resources/dns/types/custom_zone.go b/management/refactor/resources/dns/types/custom_zone.go new file mode 100644 index 000000000..25dbc58ad --- /dev/null +++ b/management/refactor/resources/dns/types/custom_zone.go @@ -0,0 +1,9 @@ +package types + +// CustomZone represents a custom zone to be resolved by the dns server +type CustomZone struct { + // Domain is the zone's domain + Domain string + // Records custom zone records + Records []SimpleRecord +} diff --git a/management/refactor/resources/dns/types/name_server.go b/management/refactor/resources/dns/types/name_server.go new file mode 100644 index 000000000..74983600f --- /dev/null +++ b/management/refactor/resources/dns/types/name_server.go @@ -0,0 +1,75 @@ +package types + +import "net/netip" + +// NameServerType nameserver type +type NameServerType int + +// NameServer represents a DNS nameserver +type NameServer struct { + // IP address of nameserver + IP netip.Addr + // NSType nameserver type + NSType NameServerType + // Port nameserver listening port + Port int +} + +// Copy copies a nameserver object +func (n *NameServer) Copy() *NameServer { + return &NameServer{ + IP: n.IP, + NSType: n.NSType, + Port: n.Port, + } +} + +// IsEqual compares one nameserver with the other +func (n *NameServer) IsEqual(other *NameServer) bool { + return other.IP == n.IP && + other.NSType == n.NSType && + other.Port == n.Port +} + +func compareNameServerList(list, other []NameServer) bool { + if len(list) != len(other) { + return false + } + + for _, ns := range list { + if !containsNameServer(ns, other) { + return false + } + } + + return true +} + +func containsNameServer(element NameServer, list []NameServer) bool { + for _, ns := range list { + if ns.IsEqual(&element) { + return true + } + } + return false +} + +func compareGroupsList(list, other []string) bool { + if len(list) != len(other) { + return false + } + for _, id := range list { + match := false + for _, otherID := range other { + if id == otherID { + match = true + break + } + } + if !match { + return false + } + } + + return true +} diff --git a/management/refactor/resources/dns/types/name_server_group.go b/management/refactor/resources/dns/types/name_server_group.go new file mode 100644 index 000000000..c3ba40af8 --- /dev/null +++ b/management/refactor/resources/dns/types/name_server_group.go @@ -0,0 +1,65 @@ +package types + +type NameServerGroup interface { +} + +type DefaultNameServerGroup struct { + // ID identifier of group + ID string `gorm:"primaryKey"` + // AccountID is a reference to Account that this object belongs + AccountID string `gorm:"index"` + // Name group name + Name string + // Description group description + Description string + // NameServers list of nameservers + NameServers []NameServer `gorm:"serializer:json"` + // Groups list of peer group IDs to distribute the nameservers information + Groups []string `gorm:"serializer:json"` + // Primary indicates that the nameserver group is the primary resolver for any dns query + Primary bool + // Domains indicate the dns query domains to use with this nameserver group + Domains []string `gorm:"serializer:json"` + // Enabled group status + Enabled bool + // SearchDomainsEnabled indicates whether to add match domains to search domains list or not + SearchDomainsEnabled bool +} + +// EventMeta returns activity event meta related to the nameserver group +func (g *DefaultNameServerGroup) EventMeta() map[string]any { + return map[string]any{"name": g.Name} +} + +// Copy copies a nameserver group object +func (g *DefaultNameServerGroup) Copy() *DefaultNameServerGroup { + nsGroup := &DefaultNameServerGroup{ + ID: g.ID, + Name: g.Name, + Description: g.Description, + NameServers: make([]NameServer, len(g.NameServers)), + Groups: make([]string, len(g.Groups)), + Enabled: g.Enabled, + Primary: g.Primary, + Domains: make([]string, len(g.Domains)), + SearchDomainsEnabled: g.SearchDomainsEnabled, + } + + copy(nsGroup.NameServers, g.NameServers) + copy(nsGroup.Groups, g.Groups) + copy(nsGroup.Domains, g.Domains) + + return nsGroup +} + +// IsEqual compares one nameserver group with the other +func (g *DefaultNameServerGroup) IsEqual(other *DefaultNameServerGroup) bool { + return other.ID == g.ID && + other.Name == g.Name && + other.Description == g.Description && + other.Primary == g.Primary && + other.SearchDomainsEnabled == g.SearchDomainsEnabled && + compareNameServerList(g.NameServers, other.NameServers) && + compareGroupsList(g.Groups, other.Groups) && + compareGroupsList(g.Domains, other.Domains) +} diff --git a/management/refactor/resources/dns/types/settings.go b/management/refactor/resources/dns/types/settings.go new file mode 100644 index 000000000..705cdfc57 --- /dev/null +++ b/management/refactor/resources/dns/types/settings.go @@ -0,0 +1,7 @@ +package types + +type Settings interface { +} + +type DefaultSettings struct { +} diff --git a/management/refactor/resources/dns/types/simpe_record.go b/management/refactor/resources/dns/types/simpe_record.go new file mode 100644 index 000000000..9e4d797ba --- /dev/null +++ b/management/refactor/resources/dns/types/simpe_record.go @@ -0,0 +1,53 @@ +package types + +import ( + "fmt" + "net" + + "github.com/miekg/dns" +) + +// SimpleRecord provides a simple DNS record specification for CNAME, A and AAAA records +type SimpleRecord struct { + // Name domain name + Name string + // Type of record, 1 for A, 5 for CNAME, 28 for AAAA. see https://pkg.go.dev/github.com/miekg/dns@v1.1.41#pkg-constants + Type int + // Class dns class, currently use the DefaultClass for all records + Class string + // TTL time-to-live for the record + TTL int + // RData is the actual value resolved in a dns query + RData string +} + +// String returns a string of the simple record formatted as: +// +func (s SimpleRecord) String() string { + fqdn := dns.Fqdn(s.Name) + return fmt.Sprintf("%s %d %s %s %s", fqdn, s.TTL, s.Class, dns.Type(s.Type).String(), s.RData) +} + +// Len returns the length of the RData field, based on its type +func (s SimpleRecord) Len() uint16 { + emptyString := s.RData == "" + switch s.Type { + case 1: + if emptyString { + return 0 + } + return net.IPv4len + case 5: + if emptyString || s.RData == "." { + return 1 + } + return uint16(len(s.RData) + 1) + case 28: + if emptyString { + return 0 + } + return net.IPv6len + default: + return 0 + } +} diff --git a/management/refactor/resources/groups/api.go b/management/refactor/resources/groups/api.go new file mode 100644 index 000000000..74900adea --- /dev/null +++ b/management/refactor/resources/groups/api.go @@ -0,0 +1 @@ +package groups diff --git a/management/refactor/resources/groups/manager.go b/management/refactor/resources/groups/manager.go new file mode 100644 index 000000000..74900adea --- /dev/null +++ b/management/refactor/resources/groups/manager.go @@ -0,0 +1 @@ +package groups diff --git a/management/refactor/resources/groups/repository.go b/management/refactor/resources/groups/repository.go new file mode 100644 index 000000000..74900adea --- /dev/null +++ b/management/refactor/resources/groups/repository.go @@ -0,0 +1 @@ +package groups diff --git a/management/refactor/resources/groups/types/group.go b/management/refactor/resources/groups/types/group.go new file mode 100644 index 000000000..2fb0f627c --- /dev/null +++ b/management/refactor/resources/groups/types/group.go @@ -0,0 +1,23 @@ +package types + +type Group interface { +} + +type DefaultGroup struct { + // ID of the group + ID string + + // AccountID is a reference to Account that this object belongs + AccountID string `json:"-" gorm:"index"` + + // Name visible in the UI + Name string + + // Issued of the group + Issued string + + // Peers list of the group + Peers []string `gorm:"serializer:json"` + + IntegrationReference IntegrationReference `gorm:"embedded;embeddedPrefix:integration_ref_"` +} diff --git a/management/refactor/resources/network/api.go b/management/refactor/resources/network/api.go new file mode 100644 index 000000000..1ae2e9d50 --- /dev/null +++ b/management/refactor/resources/network/api.go @@ -0,0 +1 @@ +package network diff --git a/management/refactor/resources/network/manager.go b/management/refactor/resources/network/manager.go new file mode 100644 index 000000000..ac7533503 --- /dev/null +++ b/management/refactor/resources/network/manager.go @@ -0,0 +1,19 @@ +package network + +import "github.com/netbirdio/netbird/management/refactor/resources/network/types" + +type Manager interface { + GetNetwork(accountID string) (types.Network, error) +} + +type DefaultManager struct { +} + +func NewDefaultManager() *DefaultManager { + return &DefaultManager{} +} + +func (d DefaultManager) GetNetwork(accountID string) (types.Network, error) { + // TODO implement me + panic("implement me") +} diff --git a/management/refactor/resources/network/repository.go b/management/refactor/resources/network/repository.go new file mode 100644 index 000000000..1ae2e9d50 --- /dev/null +++ b/management/refactor/resources/network/repository.go @@ -0,0 +1 @@ +package network diff --git a/management/refactor/resources/network/types/network.go b/management/refactor/resources/network/types/network.go new file mode 100644 index 000000000..e24bb67e2 --- /dev/null +++ b/management/refactor/resources/network/types/network.go @@ -0,0 +1,70 @@ +package types + +import ( + "math/rand" + "net" + "sync" + "time" + + "github.com/c-robinson/iplib" + "github.com/rs/xid" +) + +const ( + // SubnetSize is a size of the subnet of the global network, e.g. 100.77.0.0/16 + SubnetSize = 16 + // NetSize is a global network size 100.64.0.0/10 + NetSize = 10 +) + +type Network struct { + Identifier string `json:"id"` + Net net.IPNet `gorm:"serializer:gob"` + Dns string + // Serial is an ID that increments by 1 when any change to the network happened (e.g. new peer has been added). + // Used to synchronize state to the client apps. + Serial uint64 + + mu sync.Mutex `json:"-" gorm:"-"` +} + +// NewNetwork creates a new Network initializing it with a Serial=0 +// It takes a random /16 subnet from 100.64.0.0/10 (64 different subnets) +func NewNetwork() *Network { + + n := iplib.NewNet4(net.ParseIP("100.64.0.0"), NetSize) + sub, _ := n.Subnet(SubnetSize) + + s := rand.NewSource(time.Now().Unix()) + r := rand.New(s) + intn := r.Intn(len(sub)) + + return &Network{ + Identifier: xid.New().String(), + Net: sub[intn].IPNet, + Dns: "", + Serial: 0} +} + +// IncSerial increments Serial by 1 reflecting that the network state has been changed +func (n *Network) IncSerial() { + n.mu.Lock() + defer n.mu.Unlock() + n.Serial++ +} + +// CurrentSerial returns the Network.Serial of the network (latest state id) +func (n *Network) CurrentSerial() uint64 { + n.mu.Lock() + defer n.mu.Unlock() + return n.Serial +} + +func (n *Network) Copy() *Network { + return &Network{ + Identifier: n.Identifier, + Net: n.Net, + Dns: n.Dns, + Serial: n.Serial, + } +} diff --git a/management/refactor/resources/network/types/network_map.go b/management/refactor/resources/network/types/network_map.go new file mode 100644 index 000000000..ac8f7cded --- /dev/null +++ b/management/refactor/resources/network/types/network_map.go @@ -0,0 +1,18 @@ +package types + +import ( + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types" + routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types" + + nbdns "github.com/netbirdio/netbird/dns" +) + +type NetworkMap struct { + Peers []*peerTypes.Peer + Network *Network + Routes []*routeTypes.Route + DNSConfig nbdns.Config + OfflinePeers []*peerTypes.Peer + FirewallRules []*policyTypes.FirewallRule +} diff --git a/management/refactor/resources/peers/api.go b/management/refactor/resources/peers/api.go new file mode 100644 index 000000000..d4b765dfe --- /dev/null +++ b/management/refactor/resources/peers/api.go @@ -0,0 +1,317 @@ +package peers + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/gorilla/mux" + + http2 "github.com/netbirdio/netbird/management/refactor/api/http" + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + "github.com/netbirdio/netbird/management/refactor/store" + "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" + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/status" +) + +func RegisterPeersEndpoints(manager Manager, router *mux.Router) { + peersHandler := NewDefaultPeersHandler(manager, apiHandler.AuthCfg) + router.HandleFunc("/peers", peersHandler.GetAllPeers).Methods("GET", "OPTIONS") + router.HandleFunc("/peers/{peerId}", peersHandler.HandlePeer). + Methods("GET", "PUT", "DELETE", "OPTIONS") +} + +// DefaultPeersHandler is a handler that returns peers of the account +type DefaultPeersHandler struct { + peersManager Manager + store store.Store + claimsExtractor *jwtclaims.ClaimsExtractor +} + +// NewDefaultPeersHandler creates a new PeersHandler HTTP handler +func NewDefaultPeersHandler(manager Manager, authCfg AuthCfg) *DefaultPeersHandler { + return &DefaultPeersHandler{ + peersManager: manager, + claimsExtractor: jwtclaims.NewClaimsExtractor( + jwtclaims.WithAudience(authCfg.Audience), + jwtclaims.WithUserIDClaim(authCfg.UserIDClaim), + ), + } +} + +func (h *DefaultPeersHandler) checkPeerStatus(peer *peerTypes.Peer) (*peerTypes.Peer, error) { + peerToReturn := peer.Copy() + if peer.Status.Connected { + // Although we have online status in store we do not yet have an updated channel so have to show it as disconnected + // This may happen after server restart when not all peers are yet connected + if !h.accountManager.HasConnectedChannel(peer.ID) { + peerToReturn.Status.Connected = false + } + } + + return peerToReturn, nil +} + +func (h *DefaultPeersHandler) getPeer(account *server.Account, peerID, userID string, w http.ResponseWriter) { + peer, err := h.peersManager.GetPeerByID(account.Id, peerID, userID) + if err != nil { + util.WriteError(err, w) + return + } + + peerToReturn, err := h.checkPeerStatus(peer) + if err != nil { + util.WriteError(err, w) + return + } + dnsDomain := h.accountManager.GetDNSDomain() + + groupsInfo := toGroupsInfo(account.Groups, peer.ID) + + netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain()) + accessiblePeers := toAccessiblePeers(netMap, dnsDomain) + + util.WriteJSONObject(w, toSinglePeerResponse(peerToReturn, groupsInfo, dnsDomain, accessiblePeers)) +} + +func (h *DefaultPeersHandler) updatePeer(account *server.Account, user *server.User, peerID string, w http.ResponseWriter, r *http.Request) { + req := &api.PeerRequest{} + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w) + return + } + + update := &nbpeer.Peer{ID: peerID, SSHEnabled: req.SshEnabled, Name: req.Name, + LoginExpirationEnabled: req.LoginExpirationEnabled} + + if req.ApprovalRequired != nil { + update.Status = &nbpeer.PeerStatus{RequiresApproval: *req.ApprovalRequired} + } + + peer, err := h.accountManager.UpdatePeer(account.Id, user.Id, update) + if err != nil { + util.WriteError(err, w) + return + } + dnsDomain := h.accountManager.GetDNSDomain() + + groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID) + + netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain()) + accessiblePeers := toAccessiblePeers(netMap, dnsDomain) + + util.WriteJSONObject(w, toSinglePeerResponse(peer, groupMinimumInfo, dnsDomain, accessiblePeers)) +} + +func (h *DefaultPeersHandler) deletePeer(accountID, userID string, peerID string, w http.ResponseWriter) { + err := h.accountManager.DeletePeer(accountID, peerID, userID) + if err != nil { + util.WriteError(err, w) + return + } + util.WriteJSONObject(w, http2.EmptyObject{}) +} + +// HandlePeer handles all peer requests for GET, PUT and DELETE operations +func (h *DefaultPeersHandler) HandlePeer(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) + peerID := vars["peerId"] + if len(peerID) == 0 { + util.WriteError(status.Errorf(status.InvalidArgument, "invalid peer ID"), w) + return + } + + switch r.Method { + case http.MethodDelete: + h.deletePeer(account.Id, user.Id, peerID, w) + return + case http.MethodPut: + h.updatePeer(account, user, peerID, w, r) + return + case http.MethodGet: + h.getPeer(account, peerID, user.Id, w) + return + default: + util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w) + } +} + +// GetAllPeers returns a list of all peers associated with a provided account +func (h *DefaultPeersHandler) GetAllPeers(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + claims := h.claimsExtractor.FromRequestContext(r) + account, user, err := h.accountManager.GetAccountFromToken(claims) + if err != nil { + util.WriteError(err, w) + return + } + + peers, err := h.accountManager.GetPeers(account.Id, user.Id) + if err != nil { + util.WriteError(err, w) + return + } + + dnsDomain := h.accountManager.GetDNSDomain() + + respBody := make([]*api.PeerBatch, 0, len(peers)) + for _, peer := range peers { + peerToReturn, err := h.checkPeerStatus(peer) + if err != nil { + util.WriteError(err, w) + return + } + groupMinimumInfo := toGroupsInfo(account.Groups, peer.ID) + + accessiblePeerNumbers := h.accessiblePeersNumber(account, peer.ID) + + respBody = append(respBody, toPeerListItemResponse(peerToReturn, groupMinimumInfo, dnsDomain, accessiblePeerNumbers)) + } + util.WriteJSONObject(w, respBody) + return + default: + util.WriteError(status.Errorf(status.NotFound, "unknown METHOD"), w) + } +} + +func (h *DefaultPeersHandler) accessiblePeersNumber(account *server.Account, peerID string) int { + netMap := account.GetPeerNetworkMap(peerID, h.accountManager.GetDNSDomain()) + return len(netMap.Peers) + len(netMap.OfflinePeers) +} + +func toAccessiblePeers(netMap *server.NetworkMap, dnsDomain string) []api.AccessiblePeer { + accessiblePeers := make([]api.AccessiblePeer, 0, len(netMap.Peers)+len(netMap.OfflinePeers)) + for _, p := range netMap.Peers { + ap := api.AccessiblePeer{ + Id: p.ID, + Name: p.Name, + Ip: p.IP.String(), + DnsLabel: fqdn(p, dnsDomain), + UserId: p.UserID, + } + accessiblePeers = append(accessiblePeers, ap) + } + + for _, p := range netMap.OfflinePeers { + ap := api.AccessiblePeer{ + Id: p.ID, + Name: p.Name, + Ip: p.IP.String(), + DnsLabel: fqdn(p, dnsDomain), + UserId: p.UserID, + } + accessiblePeers = append(accessiblePeers, ap) + } + return accessiblePeers +} + +func toGroupsInfo(groups map[string]*server.Group, peerID string) []api.GroupMinimum { + var groupsInfo []api.GroupMinimum + groupsChecked := make(map[string]struct{}) + for _, group := range groups { + _, ok := groupsChecked[group.ID] + if ok { + continue + } + groupsChecked[group.ID] = struct{}{} + for _, pk := range group.Peers { + if pk == peerID { + info := api.GroupMinimum{ + Id: group.ID, + Name: group.Name, + PeersCount: len(group.Peers), + } + groupsInfo = append(groupsInfo, info) + break + } + } + } + return groupsInfo +} + +func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeer []api.AccessiblePeer) *api.Peer { + osVersion := peer.Meta.OSVersion + if osVersion == "" { + osVersion = peer.Meta.Core + } + + return &api.Peer{ + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeers: accessiblePeer, + ApprovalRequired: &peer.Status.RequiresApproval, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + } +} + +func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsDomain string, accessiblePeersCount int) *api.PeerBatch { + osVersion := peer.Meta.OSVersion + if osVersion == "" { + osVersion = peer.Meta.Core + } + + return &api.PeerBatch{ + Id: peer.ID, + Name: peer.Name, + Ip: peer.IP.String(), + ConnectionIp: peer.Location.ConnectionIP.String(), + Connected: peer.Status.Connected, + LastSeen: peer.Status.LastSeen, + Os: fmt.Sprintf("%s %s", peer.Meta.OS, osVersion), + KernelVersion: peer.Meta.KernelVersion, + GeonameId: int(peer.Location.GeoNameID), + Version: peer.Meta.WtVersion, + Groups: groupsInfo, + SshEnabled: peer.SSHEnabled, + Hostname: peer.Meta.Hostname, + UserId: peer.UserID, + UiVersion: peer.Meta.UIVersion, + DnsLabel: fqdn(peer, dnsDomain), + LoginExpirationEnabled: peer.LoginExpirationEnabled, + LastLogin: peer.LastLogin, + LoginExpired: peer.Status.LoginExpired, + AccessiblePeersCount: accessiblePeersCount, + ApprovalRequired: &peer.Status.RequiresApproval, + CountryCode: peer.Location.CountryCode, + CityName: peer.Location.CityName, + } +} + +func fqdn(peer *nbpeer.Peer, dnsDomain string) string { + fqdn := peer.FQDN(dnsDomain) + if fqdn == "" { + return peer.DNSLabel + } else { + return fqdn + } +} diff --git a/management/refactor/resources/peers/manager.go b/management/refactor/resources/peers/manager.go new file mode 100644 index 000000000..aad5d62a2 --- /dev/null +++ b/management/refactor/resources/peers/manager.go @@ -0,0 +1,51 @@ +package peers + +import ( + "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + "github.com/netbirdio/netbird/management/refactor/resources/settings" +) + +type Manager interface { + GetPeerByPubKey(pubKey string) (types.Peer, error) + GetPeerByID(id string) (types.Peer, error) + GetNetworkPeerByID(id string) (types.Peer, error) + GetNetworkPeersInAccount(id string) ([]types.Peer, error) +} + +type DefaultManager struct { + repository Repository + settingsManager settings.Manager +} + +func NewDefaultManager(repository Repository, settingsManager settings.Manager) *DefaultManager { + return &DefaultManager{ + repository: repository, + settingsManager: settingsManager, + } +} + +func (dm *DefaultManager) GetNetworkPeerByID(id string) (types.Peer, error) { + return dm.repository.FindPeerByID(id) +} + +func (dm *DefaultManager) GetNetworkPeersInAccount(accountId string) ([]types.Peer, error) { + defaultPeers, err := dm.repository.FindAllPeersInAccount(accountId) + if err != nil { + return nil, err + } + + peers := make([]types.Peer, len(defaultPeers)) + for _, dp := range defaultPeers { + peers = append(peers, dp) + } + + return peers, nil +} + +func (dm *DefaultManager) GetPeerByPubKey(pubKey string) (types.Peer, error) { + return dm.repository.FindPeerByPubKey(pubKey) +} + +func (dm *DefaultManager) GetPeerByID(id string) (types.Peer, error) { + return dm.repository.FindPeerByID(id) +} diff --git a/management/refactor/resources/peers/repository.go b/management/refactor/resources/peers/repository.go new file mode 100644 index 000000000..dfcfbbce7 --- /dev/null +++ b/management/refactor/resources/peers/repository.go @@ -0,0 +1,10 @@ +package peers + +import "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + +type Repository interface { + FindPeerByPubKey(pubKey string) (types.Peer, error) + FindPeerByID(id string) (types.Peer, error) + FindAllPeersInAccount(id string) ([]types.Peer, error) + UpdatePeer(peer types.Peer) error +} diff --git a/management/refactor/peers/peer.go b/management/refactor/resources/peers/types/peer.go similarity index 90% rename from management/refactor/peers/peer.go rename to management/refactor/resources/peers/types/peer.go index 82b6fa15e..9292ef1cd 100644 --- a/management/refactor/peers/peer.go +++ b/management/refactor/resources/peers/types/peer.go @@ -1,4 +1,4 @@ -package peers +package types import ( "fmt" @@ -33,6 +33,7 @@ type Peer interface { FQDN(dnsDomain string) string EventMeta(dnsDomain string) map[string]any LoginExpired(expiresIn time.Duration) (bool, time.Duration) + Copy() Peer } // Peer represents a machine connected to the network. @@ -48,11 +49,15 @@ type DefaultPeer struct { SetupKey string // IP address of the Peer IP net.IP `gorm:"uniqueIndex:idx_peers_account_id_ip"` + // Meta is a Peer system meta data + Meta PeerSystemMeta `gorm:"embedded;embeddedPrefix:meta_"` // Name is peer's name (machine name) Name string // DNSLabel is the parsed peer name for domain resolution. It is used to form an FQDN by appending the account's // domain to the peer label. e.g. peer-dns-label.netbird.cloud DNSLabel string + // Status peer's management connection status + Status *PeerStatus `gorm:"embedded;embeddedPrefix:peer_status_"` // The user ID that registered the peer UserID string // SSHKey is a public SSH key of the peer @@ -64,8 +69,20 @@ type DefaultPeer struct { LoginExpirationEnabled bool // LastLogin the time when peer performed last login operation LastLogin time.Time + // CreatedAt records the time the peer was created + CreatedAt time.Time // Indicate ephemeral peer attribute Ephemeral bool + // Geo location based on connection IP + Location Location `gorm:"embedded;embeddedPrefix:location_"` +} + +// Location is a geo location information of a Peer based on public connection IP +type Location struct { + ConnectionIP net.IP // from grpc peer or reverse proxy headers depends on setup + CountryCode string + CityName string + GeoNameID uint // city level geoname id } // PeerLogin used as a data object between the gRPC API and AccountManager on Login request. diff --git a/management/refactor/resources/peers/types/peer_status.go b/management/refactor/resources/peers/types/peer_status.go new file mode 100644 index 000000000..2eb037ad2 --- /dev/null +++ b/management/refactor/resources/peers/types/peer_status.go @@ -0,0 +1,24 @@ +package types + +import "time" + +// Copy PeerStatus +func (p *PeerStatus) Copy() *PeerStatus { + return &PeerStatus{ + LastSeen: p.LastSeen, + Connected: p.Connected, + LoginExpired: p.LoginExpired, + RequiresApproval: p.RequiresApproval, + } +} + +type PeerStatus struct { //nolint:revive + // LastSeen is the last time peer was connected to the management service + LastSeen time.Time + // Connected indicates whether peer is connected to the management service or not + Connected bool + // LoginExpired + LoginExpired bool + // RequiresApproval indicates whether peer requires approval or not + RequiresApproval bool +} diff --git a/management/refactor/resources/peers/types/peer_system_meta.go b/management/refactor/resources/peers/types/peer_system_meta.go new file mode 100644 index 000000000..0fe30adf7 --- /dev/null +++ b/management/refactor/resources/peers/types/peer_system_meta.go @@ -0,0 +1,69 @@ +package types + +import "net/netip" + +// NetworkAddress is the IP address with network and MAC address of a network interface +type NetworkAddress struct { + NetIP netip.Prefix `gorm:"serializer:json"` + Mac string +} + +// Environment is a system environment information +type Environment struct { + Cloud string + Platform string +} + +// PeerSystemMeta is a metadata of a Peer machine system +type PeerSystemMeta struct { //nolint:revive + Hostname string + GoOS string + Kernel string + Core string + Platform string + OS string + OSVersion string + WtVersion string + UIVersion string + KernelVersion string + NetworkAddresses []NetworkAddress `gorm:"serializer:json"` + SystemSerialNumber string + SystemProductName string + SystemManufacturer string + Environment Environment `gorm:"serializer:json"` +} + +func (p PeerSystemMeta) isEqual(other PeerSystemMeta) bool { + if len(p.NetworkAddresses) != len(other.NetworkAddresses) { + return false + } + + for _, addr := range p.NetworkAddresses { + var found bool + for _, oAddr := range other.NetworkAddresses { + if addr.Mac == oAddr.Mac && addr.NetIP == oAddr.NetIP { + found = true + continue + } + } + if !found { + return false + } + } + + return p.Hostname == other.Hostname && + p.GoOS == other.GoOS && + p.Kernel == other.Kernel && + p.KernelVersion == other.KernelVersion && + p.Core == other.Core && + p.Platform == other.Platform && + p.OS == other.OS && + p.OSVersion == other.OSVersion && + p.WtVersion == other.WtVersion && + p.UIVersion == other.UIVersion && + p.SystemSerialNumber == other.SystemSerialNumber && + p.SystemProductName == other.SystemProductName && + p.SystemManufacturer == other.SystemManufacturer && + p.Environment.Cloud == other.Environment.Cloud && + p.Environment.Platform == other.Environment.Platform +} diff --git a/management/refactor/resources/policies/api.go b/management/refactor/resources/policies/api.go new file mode 100644 index 000000000..a01ffa69c --- /dev/null +++ b/management/refactor/resources/policies/api.go @@ -0,0 +1 @@ +package policies diff --git a/management/refactor/policies/manager.go b/management/refactor/resources/policies/manager.go similarity index 57% rename from management/refactor/policies/manager.go rename to management/refactor/resources/policies/manager.go index 8f9d4f12f..9b3d2595d 100644 --- a/management/refactor/policies/manager.go +++ b/management/refactor/resources/policies/manager.go @@ -1,24 +1,27 @@ package policies -import "github.com/netbirdio/netbird/management/refactor/peers" +import ( + "github.com/netbirdio/netbird/management/refactor/resources/peers" + "github.com/netbirdio/netbird/management/refactor/resources/peers/types" +) type Manager interface { - GetAccessiblePeersAndFirewallRules(peerID string) (peers []peers.Peer, firewallRules []*FirewallRule) + GetAccessiblePeersAndFirewallRules(peerID string) (peers []types.Peer, firewallRules []*FirewallRule) } type DefaultManager struct { - repository repository + repository Repository peerManager peers.Manager } -func NewDefaultManager(repository repository, peerManager peers.Manager) *DefaultManager { +func NewDefaultManager(repository Repository, peerManager peers.Manager) *DefaultManager { return &DefaultManager{ repository: repository, peerManager: peerManager, } } -func (dm *DefaultManager) GetAccessiblePeersAndFirewallRules(peerID string) (peers []peers.Peer, firewallRules []*FirewallRule) { +func (dm *DefaultManager) GetAccessiblePeersAndFirewallRules(peerID string) (peers []types.Peer, firewallRules []*FirewallRule) { peer, err := dm.peerManager.GetPeerByID(peerID) if err != nil { return nil, nil diff --git a/management/refactor/resources/policies/posture/api.go b/management/refactor/resources/policies/posture/api.go new file mode 100644 index 000000000..7a793b351 --- /dev/null +++ b/management/refactor/resources/policies/posture/api.go @@ -0,0 +1 @@ +package posture diff --git a/management/refactor/resources/policies/posture/manager.go b/management/refactor/resources/policies/posture/manager.go new file mode 100644 index 000000000..7a793b351 --- /dev/null +++ b/management/refactor/resources/policies/posture/manager.go @@ -0,0 +1 @@ +package posture diff --git a/management/refactor/resources/policies/posture/repository.go b/management/refactor/resources/policies/posture/repository.go new file mode 100644 index 000000000..7a793b351 --- /dev/null +++ b/management/refactor/resources/policies/posture/repository.go @@ -0,0 +1 @@ +package posture diff --git a/management/refactor/policies/repository.go b/management/refactor/resources/policies/repository.go similarity index 100% rename from management/refactor/policies/repository.go rename to management/refactor/resources/policies/repository.go diff --git a/management/refactor/resources/policies/types/firewall_rule.go b/management/refactor/resources/policies/types/firewall_rule.go new file mode 100644 index 000000000..8a8fe1b5c --- /dev/null +++ b/management/refactor/resources/policies/types/firewall_rule.go @@ -0,0 +1,19 @@ +package types + +// FirewallRule is a rule of the firewall. +type FirewallRule struct { + // PeerIP of the peer + PeerIP string + + // Direction of the traffic + Direction int + + // Action of the traffic + Action string + + // Protocol of the traffic + Protocol string + + // Port of the traffic + Port string +} diff --git a/management/refactor/resources/policies/types/policy.go b/management/refactor/resources/policies/types/policy.go new file mode 100644 index 000000000..32dcea232 --- /dev/null +++ b/management/refactor/resources/policies/types/policy.go @@ -0,0 +1,13 @@ +package types + +type Policy interface { + GetID() string +} + +type DefaultPolicy struct { + ID string +} + +func (dp *DefaultPolicy) GetID() string { + return dp.ID +} diff --git a/management/refactor/resources/policies/types/policy_rule.go b/management/refactor/resources/policies/types/policy_rule.go new file mode 100644 index 000000000..df12de795 --- /dev/null +++ b/management/refactor/resources/policies/types/policy_rule.go @@ -0,0 +1,13 @@ +package types + +type PolicyRule interface { + GetID() string +} + +type DefaultPolicyRule struct { + ID string +} + +func (dpr *DefaultPolicyRule) GetID() string { + return dpr.ID +} diff --git a/management/refactor/resources/routes/api.go b/management/refactor/resources/routes/api.go new file mode 100644 index 000000000..0db51ae52 --- /dev/null +++ b/management/refactor/resources/routes/api.go @@ -0,0 +1 @@ +package routes diff --git a/management/refactor/resources/routes/manager.go b/management/refactor/resources/routes/manager.go new file mode 100644 index 000000000..60ed1120f --- /dev/null +++ b/management/refactor/resources/routes/manager.go @@ -0,0 +1,100 @@ +package routes + +import ( + log "github.com/sirupsen/logrus" + + "github.com/netbirdio/netbird/management/refactor/resources/peers" + "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types" + "github.com/netbirdio/netbird/route" +) + +type Manager interface { + GetRoutesToSync(peerID string, peersToConnect []*types.Peer, accountID string) []*routeTypes.Route +} + +type DefaultManager struct { + repository Repository + peersManager peers.Manager +} + +func NewDefaultManager(repository Repository, peersManager peers.Manager) *DefaultManager { + return &DefaultManager{ + repository: repository, + peersManager: peersManager, + } +} + +func (d DefaultManager) GetRoutesToSync(peerID string, peersToConnect []*types.Peer) []*routeTypes.Route { + routes, peerDisabledRoutes := d.getRoutingPeerRoutes(peerID) + peerRoutesMembership := make(lookupMap) + for _, r := range append(routes, peerDisabledRoutes...) { + peerRoutesMembership[route.GetHAUniqueID(r)] = struct{}{} + } + + groupListMap := a.getPeerGroups(peerID) + for _, peer := range aclPeers { + activeRoutes, _ := a.getRoutingPeerRoutes(peer.ID) + groupFilteredRoutes := a.filterRoutesByGroups(activeRoutes, groupListMap) + filteredRoutes := a.filterRoutesFromPeersOfSameHAGroup(groupFilteredRoutes, peerRoutesMembership) + routes = append(routes, filteredRoutes...) + } + + return routes +} + +func (d DefaultManager) getRoutingPeerRoutes(accountID, peerID string) (enabledRoutes []routeTypes.Route, disabledRoutes []routeTypes.Route) { + peer, err := d.peersManager.GetPeerByID(peerID) + if err != nil { + log.Errorf("peer %s that doesn't exist under account %s", peerID, accountID) + return nil, nil + } + + // currently we support only linux routing peers + if peer.Meta.GoOS != "linux" { + return enabledRoutes, disabledRoutes + } + + seenRoute := make(map[string]struct{}) + + takeRoute := func(r routeTypes.Route, id string) { + if _, ok := seenRoute[r.GetID()]; ok { + return + } + seenRoute[r.GetID()] = struct{}{} + + if r.IsEnabled() { + r.SetPeer(peer.GetKey()) + enabledRoutes = append(enabledRoutes, r) + return + } + disabledRoutes = append(disabledRoutes, r) + } + + for _, r := range a.Routes { + for _, groupID := range r.PeerGroups { + group := a.GetGroup(groupID) + if group == nil { + log.Errorf("route %s has peers group %s that doesn't exist under account %s", r.ID, groupID, a.Id) + continue + } + for _, id := range group.Peers { + if id != peerID { + continue + } + + newPeerRoute := r.Copy() + newPeerRoute.Peer = id + newPeerRoute.PeerGroups = nil + newPeerRoute.ID = r.ID + ":" + id // we have to provide unique route id when distribute network map + takeRoute(newPeerRoute, id) + break + } + } + if r.Peer == peerID { + takeRoute(r.Copy(), peerID) + } + } + + return enabledRoutes, disabledRoutes +} diff --git a/management/refactor/resources/routes/repository.go b/management/refactor/resources/routes/repository.go new file mode 100644 index 000000000..9513ff597 --- /dev/null +++ b/management/refactor/resources/routes/repository.go @@ -0,0 +1,4 @@ +package routes + +type Repository interface { +} diff --git a/management/refactor/resources/routes/types/route.go b/management/refactor/resources/routes/types/route.go new file mode 100644 index 000000000..80332ed27 --- /dev/null +++ b/management/refactor/resources/routes/types/route.go @@ -0,0 +1,54 @@ +package types + +import "net/netip" + +const ( + // InvalidNetwork invalid network type + InvalidNetwork NetworkType = iota + // IPv4Network IPv4 network type + IPv4Network + // IPv6Network IPv6 network type + IPv6Network +) + +// NetworkType route network type +type NetworkType int + +type Route interface { + GetID() string + IsEnabled() bool + GetPeer() string + SetPeer(string) +} + +type DefaultRoute struct { + ID string `gorm:"primaryKey"` + // AccountID is a reference to Account that this object belongs + AccountID string `gorm:"index"` + Network netip.Prefix `gorm:"serializer:gob"` + NetID string + Description string + Peer string + PeerGroups []string `gorm:"serializer:gob"` + NetworkType NetworkType + Masquerade bool + Metric int + Enabled bool + Groups []string `gorm:"serializer:json"` +} + +func (r *DefaultRoute) GetID() string { + return r.ID +} + +func (r *DefaultRoute) IsEnabled() bool { + return r.Enabled +} + +func (r *DefaultRoute) GetPeer() string { + return r.Peer +} + +func (r *DefaultRoute) SetPeer(peer string) { + r.Peer = peer +} diff --git a/management/refactor/resources/settings/api.go b/management/refactor/resources/settings/api.go new file mode 100644 index 000000000..0e66fdf1e --- /dev/null +++ b/management/refactor/resources/settings/api.go @@ -0,0 +1 @@ +package settings diff --git a/management/refactor/resources/settings/manager.go b/management/refactor/resources/settings/manager.go new file mode 100644 index 000000000..822eacaf8 --- /dev/null +++ b/management/refactor/resources/settings/manager.go @@ -0,0 +1,21 @@ +package settings + +import "github.com/netbirdio/netbird/management/refactor/resources/settings/types" + +type Manager interface { + GetSettings(accountID string) (types.Settings, error) +} + +type DefaultManager struct { + repository Repository +} + +func NewDefaultManager(repository Repository) *DefaultManager { + return &DefaultManager{ + repository: repository, + } +} + +func (dm *DefaultManager) GetSettings(accountID string) (types.Settings, error) { + return dm.repository.FindSettings(accountID) +} diff --git a/management/refactor/resources/settings/repository.go b/management/refactor/resources/settings/repository.go new file mode 100644 index 000000000..dc2a56b17 --- /dev/null +++ b/management/refactor/resources/settings/repository.go @@ -0,0 +1,7 @@ +package settings + +import "github.com/netbirdio/netbird/management/refactor/resources/settings/types" + +type Repository interface { + FindSettings(accountID string) (types.Settings, error) +} diff --git a/management/refactor/settings/settings.go b/management/refactor/resources/settings/types/settings.go similarity index 97% rename from management/refactor/settings/settings.go rename to management/refactor/resources/settings/types/settings.go index d640614d1..c40da58ac 100644 --- a/management/refactor/settings/settings.go +++ b/management/refactor/resources/settings/types/settings.go @@ -1,4 +1,4 @@ -package settings +package types import "time" diff --git a/management/refactor/resources/setup_keys/api.go b/management/refactor/resources/setup_keys/api.go new file mode 100644 index 000000000..e57ebbcf4 --- /dev/null +++ b/management/refactor/resources/setup_keys/api.go @@ -0,0 +1 @@ +package setup_keys diff --git a/management/refactor/resources/setup_keys/manager.go b/management/refactor/resources/setup_keys/manager.go new file mode 100644 index 000000000..e57ebbcf4 --- /dev/null +++ b/management/refactor/resources/setup_keys/manager.go @@ -0,0 +1 @@ +package setup_keys diff --git a/management/refactor/resources/setup_keys/repository.go b/management/refactor/resources/setup_keys/repository.go new file mode 100644 index 000000000..e57ebbcf4 --- /dev/null +++ b/management/refactor/resources/setup_keys/repository.go @@ -0,0 +1 @@ +package setup_keys diff --git a/management/refactor/resources/setup_keys/types/setup_key.go b/management/refactor/resources/setup_keys/types/setup_key.go new file mode 100644 index 000000000..10e5df2eb --- /dev/null +++ b/management/refactor/resources/setup_keys/types/setup_key.go @@ -0,0 +1,7 @@ +package types + +type SetupKey interface { +} + +type DefaultSetupKey struct { +} diff --git a/management/refactor/resources/users/api.go b/management/refactor/resources/users/api.go new file mode 100644 index 000000000..82abcb9fc --- /dev/null +++ b/management/refactor/resources/users/api.go @@ -0,0 +1 @@ +package users diff --git a/management/refactor/resources/users/manager.go b/management/refactor/resources/users/manager.go new file mode 100644 index 000000000..9665d723e --- /dev/null +++ b/management/refactor/resources/users/manager.go @@ -0,0 +1,27 @@ +package users + +import ( + "github.com/netbirdio/netbird/management/refactor/resources/peers" + "github.com/netbirdio/netbird/management/refactor/resources/users/types" +) + +type Manager interface { + GetUser(id string) (types.User, error) +} + +type DefaultManager struct { + repository Repository + peerManager peers.Manager +} + +func NewDefaultManager(repository Repository, peerManager peers.Manager) *DefaultManager { + return &DefaultManager{ + repository: repository, + peerManager: peerManager, + } +} + +func (d DefaultManager) GetUser(id string) (types.User, error) { + // TODO implement me + panic("implement me") +} diff --git a/management/refactor/resources/users/personal_access_tokens/api.go b/management/refactor/resources/users/personal_access_tokens/api.go new file mode 100644 index 000000000..372b60644 --- /dev/null +++ b/management/refactor/resources/users/personal_access_tokens/api.go @@ -0,0 +1 @@ +package personal_access_tokens diff --git a/management/refactor/resources/users/personal_access_tokens/manager.go b/management/refactor/resources/users/personal_access_tokens/manager.go new file mode 100644 index 000000000..372b60644 --- /dev/null +++ b/management/refactor/resources/users/personal_access_tokens/manager.go @@ -0,0 +1 @@ +package personal_access_tokens diff --git a/management/refactor/resources/users/personal_access_tokens/repository.go b/management/refactor/resources/users/personal_access_tokens/repository.go new file mode 100644 index 000000000..372b60644 --- /dev/null +++ b/management/refactor/resources/users/personal_access_tokens/repository.go @@ -0,0 +1 @@ +package personal_access_tokens diff --git a/management/refactor/resources/users/personal_access_tokens/types/personal_access_token.go b/management/refactor/resources/users/personal_access_tokens/types/personal_access_token.go new file mode 100644 index 000000000..ab1254f4c --- /dev/null +++ b/management/refactor/resources/users/personal_access_tokens/types/personal_access_token.go @@ -0,0 +1 @@ +package types diff --git a/management/refactor/users/repository.go b/management/refactor/resources/users/repository.go similarity index 100% rename from management/refactor/users/repository.go rename to management/refactor/resources/users/repository.go diff --git a/management/refactor/users/user.go b/management/refactor/resources/users/types/user.go similarity index 98% rename from management/refactor/users/user.go rename to management/refactor/resources/users/types/user.go index bd12c65df..cb79f4b94 100644 --- a/management/refactor/users/user.go +++ b/management/refactor/resources/users/types/user.go @@ -1,4 +1,4 @@ -package users +package types import "time" diff --git a/management/refactor/settings/manager.go b/management/refactor/settings/manager.go deleted file mode 100644 index 3ec3f42d7..000000000 --- a/management/refactor/settings/manager.go +++ /dev/null @@ -1,19 +0,0 @@ -package settings - -type Manager interface { - GetSettings(accountID string) (Settings, error) -} - -type DefaultManager struct { - repository repository -} - -func NewDefaultManager(repository repository) *DefaultManager { - return &DefaultManager{ - repository: repository, - } -} - -func (dm *DefaultManager) GetSettings(accountID string) (Settings, error) { - return dm.repository.FindSettings(accountID) -} diff --git a/management/refactor/settings/repository.go b/management/refactor/settings/repository.go deleted file mode 100644 index 380ce6153..000000000 --- a/management/refactor/settings/repository.go +++ /dev/null @@ -1,5 +0,0 @@ -package settings - -type Repository interface { - FindSettings(accountID string) (Settings, error) -} diff --git a/management/refactor/store/account.go b/management/refactor/store/account.go new file mode 100644 index 000000000..cd8c9fbfd --- /dev/null +++ b/management/refactor/store/account.go @@ -0,0 +1,50 @@ +package store + +import ( + "time" + + dnsTypes "github.com/netbirdio/netbird/management/refactor/resources/dns/types" + groupTypes "github.com/netbirdio/netbird/management/refactor/resources/groups/types" + networkTypes "github.com/netbirdio/netbird/management/refactor/resources/network/types" + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types" + routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types" + settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types" + setupKeyTypes "github.com/netbirdio/netbird/management/refactor/resources/setup_keys/types" + userTypes "github.com/netbirdio/netbird/management/refactor/resources/users/types" + "github.com/netbirdio/netbird/management/server/posture" +) + +// Account represents a unique account of the system +type DefaultAccount struct { + // we have to name column to aid as it collides with Network.Id when work with associations + Id string `gorm:"primaryKey"` + + // User.Id it was created by + CreatedBy string + CreatedAt time.Time + Domain string `gorm:"index"` + DomainCategory string + IsDomainPrimaryAccount bool + SetupKeys map[string]*setupKeyTypes.DefaultSetupKey `gorm:"-"` + SetupKeysG []setupKeyTypes.DefaultSetupKey `json:"-" gorm:"foreignKey:AccountID;references:id"` + Network *networkTypes.Network `gorm:"embedded;embeddedPrefix:network_"` + Peers map[string]*peerTypes.DefaultPeer `gorm:"-"` + PeersG []peerTypes.DefaultPeer `json:"-" gorm:"foreignKey:AccountID;references:id"` + Users map[string]*userTypes.DefaultUser `gorm:"-"` + UsersG []userTypes.DefaultUser `json:"-" gorm:"foreignKey:AccountID;references:id"` + Groups map[string]*groupTypes.DefaultGroup `gorm:"-"` + GroupsG []groupTypes.DefaultGroup `json:"-" gorm:"foreignKey:AccountID;references:id"` + Policies []*policyTypes.DefaultPolicy `gorm:"foreignKey:AccountID;references:id"` + Routes map[string]*routeTypes.DefaultRoute `gorm:"-"` + RoutesG []routeTypes.DefaultRoute `json:"-" gorm:"foreignKey:AccountID;references:id"` + NameServerGroups map[string]*dnsTypes.DefaultNameServerGroup `gorm:"-"` + NameServerGroupsG []dnsTypes.DefaultNameServerGroup `json:"-" gorm:"foreignKey:AccountID;references:id"` + DNSSettings dnsTypes.DefaultSettings `gorm:"embedded;embeddedPrefix:dns_settings_"` + PostureChecks []*posture.Checks `gorm:"foreignKey:AccountID;references:id"` + // Settings is a dictionary of Account settings + Settings *settingsTypes.DefaultSettings `gorm:"embedded;embeddedPrefix:settings_"` + // deprecated on store and api level + Rules map[string]*Rule `json:"-" gorm:"-"` + RulesG []Rule `json:"-" gorm:"-"` +} diff --git a/management/refactor/store/postgres_store.go b/management/refactor/store/postgres_store.go index 3234db818..192f7568f 100644 --- a/management/refactor/store/postgres_store.go +++ b/management/refactor/store/postgres_store.go @@ -1,8 +1,8 @@ package store import ( - "github.com/netbirdio/netbird/management/refactor/peers" - "github.com/netbirdio/netbird/management/refactor/settings" + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types" ) const ( @@ -12,27 +12,27 @@ const ( type DefaultPostgresStore struct { } -func (s *DefaultPostgresStore) FindSettings(accountID string) (settings.Settings, error) { +func (s *DefaultPostgresStore) FindSettings(accountID string) (settingsTypes.Settings, error) { // TODO implement me panic("implement me") } -func (s *DefaultPostgresStore) FindPeerByPubKey(pubKey string) (peers.Peer, error) { +func (s *DefaultPostgresStore) FindPeerByPubKey(pubKey string) (peerTypes.Peer, error) { // TODO implement me panic("implement me") } -func (s *DefaultPostgresStore) FindPeerByID(id string) (peers.Peer, error) { +func (s *DefaultPostgresStore) FindPeerByID(id string) (peerTypes.Peer, error) { // TODO implement me panic("implement me") } -func (s *DefaultPostgresStore) FindAllPeersInAccount(id string) ([]peers.Peer, error) { +func (s *DefaultPostgresStore) FindAllPeersInAccount(id string) ([]peerTypes.Peer, error) { // TODO implement me panic("implement me") } -func (s *DefaultPostgresStore) UpdatePeer(peer peers.Peer) error { +func (s *DefaultPostgresStore) UpdatePeer(peer peerTypes.Peer) error { // TODO implement me panic("implement me") } diff --git a/management/refactor/store/sqlite_store.go b/management/refactor/store/sqlite_store.go index d54b2a538..96a008517 100644 --- a/management/refactor/store/sqlite_store.go +++ b/management/refactor/store/sqlite_store.go @@ -1,6 +1,7 @@ package store import ( + "errors" "path/filepath" "runtime" "sync" @@ -12,8 +13,17 @@ import ( "gorm.io/gorm/clause" "gorm.io/gorm/logger" - "github.com/netbirdio/netbird/management/refactor/peers" - "github.com/netbirdio/netbird/management/refactor/settings" + dnsTypes "github.com/netbirdio/netbird/management/refactor/resources/dns/types" + groupTypes "github.com/netbirdio/netbird/management/refactor/resources/groups/types" + "github.com/netbirdio/netbird/management/refactor/resources/peers" + policyTypes "github.com/netbirdio/netbird/management/refactor/resources/policies/types" + routeTypes "github.com/netbirdio/netbird/management/refactor/resources/routes/types" + "github.com/netbirdio/netbird/management/refactor/resources/settings" + setupKeyTypes "github.com/netbirdio/netbird/management/refactor/resources/setup_keys/types" + userTypes "github.com/netbirdio/netbird/management/refactor/resources/users/types" + + nbpeer "github.com/netbirdio/netbird/management/server/peer" + "github.com/netbirdio/netbird/management/server/status" "github.com/netbirdio/netbird/management/server/telemetry" ) @@ -23,37 +33,13 @@ const ( // SqliteStore represents an account storage backed by a Sqlite DB persisted to disk type DefaultSqliteStore struct { - db *gorm.DB + DB *gorm.DB storeFile string accountLocks sync.Map globalAccountLock sync.Mutex metrics telemetry.AppMetrics installationPK int -} - -func (s *DefaultSqliteStore) FindSettings(accountID string) (settings.Settings, error) { - // TODO implement me - panic("implement me") -} - -func (s *DefaultSqliteStore) FindPeerByPubKey(pubKey string) (peers.Peer, error) { - // TODO implement me - panic("implement me") -} - -func (s *DefaultSqliteStore) FindPeerByID(id string) (peers.Peer, error) { - // TODO implement me - panic("implement me") -} - -func (s *DefaultSqliteStore) FindAllPeersInAccount(id string) ([]peers.Peer, error) { - // TODO implement me - panic("implement me") -} - -func (s *DefaultSqliteStore) UpdatePeer(peer peers.Peer) error { - // TODO implement me - panic("implement me") + accounts map[string]*DefaultAccount } type installation struct { @@ -63,10 +49,10 @@ type installation struct { // NewSqliteStore restores a store from the file located in the datadir func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*DefaultSqliteStore, error) { - storeStr := "store.db?cache=shared" + storeStr := "store.DB?cache=shared" if runtime.GOOS == "windows" { // Vo avoid `The process cannot access the file because it is being used by another process` on Windows - storeStr = "store.db" + storeStr = "store.DB" } file := filepath.Join(dataDir, storeStr) @@ -85,7 +71,7 @@ func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*Defau conns := runtime.NumCPU() sql.SetMaxOpenConns(conns) // TODO: make it configurable - // err = db.AutoMigrate( + // err = DB.AutoMigrate( // &SetupKey{}, &Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{}, // &Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{}, // &installation{}, @@ -94,12 +80,7 @@ func NewDefaultSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*Defau // return nil, err // } - return &DefaultSqliteStore{db: db, storeFile: file, metrics: metrics, installationPK: 1}, nil -} - -func (s *DefaultSqliteStore) GetLicense() string { - // TODO implement me - panic("implement me") + return &DefaultSqliteStore{DB: db, storeFile: file, metrics: metrics, installationPK: 1}, nil } // AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock @@ -138,17 +119,161 @@ func (s *DefaultSqliteStore) AcquireAccountLock(accountID string) (unlock func() return unlock } +func (s *DefaultSqliteStore) LoadAccount(accountID string) error { + var account DefaultAccount + result := s.DB.Model(&account). + Preload("UsersG.PATsG"). // have to be specifies as this is nester reference + Preload(clause.Associations). + First(&account, "id = ?", accountID) + if result.Error != nil { + log.Errorf("error when getting account from the store: %s", result.Error) + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return status.Errorf(status.NotFound, "account not found") + } + return status.Errorf(status.Internal, "issue getting account from store") + } + + // we have to manually preload policy rules as it seems that gorm preloading doesn't do it for us + for i, policy := range account.Policies { + var rules []*policyTypes.DefaultPolicyRule + err := s.DB.Model(&policyTypes.DefaultPolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error + if err != nil { + return status.Errorf(status.NotFound, "rule not found") + } + account.Policies[i].Rules = rules + } + + account.SetupKeys = make(map[string]*setupKeyTypes.DefaultSetupKey, len(account.SetupKeysG)) + for _, key := range account.SetupKeysG { + account.SetupKeys[key.Key] = key.Copy() + } + account.SetupKeysG = nil + + account.Peers = make(map[string]*nbpeer.Peer, len(account.PeersG)) + for _, peer := range account.PeersG { + account.Peers[peer.ID] = peer.Copy() + } + account.PeersG = nil + + account.Users = make(map[string]*userTypes.DefaultUser, len(account.UsersG)) + for _, user := range account.UsersG { + user.PATs = make(map[string]*PersonalAccessToken, len(user.PATs)) + for _, pat := range user.PATsG { + user.PATs[pat.ID] = pat.Copy() + } + account.Users[user.Id] = user.Copy() + } + account.UsersG = nil + + account.Groups = make(map[string]*groupTypes.DefaultGroup, len(account.GroupsG)) + for _, group := range account.GroupsG { + account.Groups[group.ID] = group.Copy() + } + account.GroupsG = nil + + account.Routes = make(map[string]*routeTypes.DefaultRoute, len(account.RoutesG)) + for _, route := range account.RoutesG { + account.Routes[route.ID] = route.Copy() + } + account.RoutesG = nil + + account.NameServerGroups = make(map[string]*dnsTypes.DefaultNameServerGroup, len(account.NameServerGroupsG)) + for _, ns := range account.NameServerGroupsG { + account.NameServerGroups[ns.ID] = ns.Copy() + } + account.NameServerGroupsG = nil + + s.accounts[account.Id] = &account + + return nil +} + +func (s *DefaultSqliteStore) WriteAccount(accountID string) error { + start := time.Now() + + account, ok := s.accounts[accountID] + if !ok { + return status.Errorf(status.NotFound, "account not found") + } + + for _, key := range account.SetupKeys { + account.SetupKeysG = append(account.SetupKeysG, *key) + } + + for id, peer := range account.Peers { + peer.ID = id + account.PeersG = append(account.PeersG, *peer) + } + + for id, user := range account.Users { + user.Id = id + for id, pat := range user.PATs { + pat.ID = id + user.PATsG = append(user.PATsG, *pat) + } + account.UsersG = append(account.UsersG, *user) + } + + for id, group := range account.Groups { + group.ID = id + account.GroupsG = append(account.GroupsG, *group) + } + + for id, route := range account.Routes { + route.ID = id + account.RoutesG = append(account.RoutesG, *route) + } + + for id, ns := range account.NameServerGroups { + ns.ID = id + account.NameServerGroupsG = append(account.NameServerGroupsG, *ns) + } + + err := s.DB.Transaction(func(tx *gorm.DB) error { + result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id) + if result.Error != nil { + return result.Error + } + + result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id) + if result.Error != nil { + return result.Error + } + + result = tx.Select(clause.Associations).Delete(account) + if result.Error != nil { + return result.Error + } + + result = tx. + Session(&gorm.Session{FullSaveAssociations: true}). + Clauses(clause.OnConflict{UpdateAll: true}).Create(account) + if result.Error != nil { + return result.Error + } + return nil + }) + + took := time.Since(start) + if s.metrics != nil { + s.metrics.StoreMetrics().CountPersistenceDuration(took) + } + log.Debugf("took %d ms to persist an account to the SQLite", took.Milliseconds()) + + return err +} + func (s *DefaultSqliteStore) SaveInstallationID(ID string) error { installation := installation{InstallationIDValue: ID} installation.ID = uint(s.installationPK) - return s.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error + return s.DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error } func (s *DefaultSqliteStore) GetInstallationID() string { var installation installation - if result := s.db.First(&installation, "id = ?", s.installationPK); result.Error != nil { + if result := s.DB.First(&installation, "id = ?", s.installationPK); result.Error != nil { return "" } @@ -164,3 +289,57 @@ func (s *DefaultSqliteStore) Close() error { func (s *DefaultSqliteStore) GetStoreEngine() StoreEngine { return SqliteStoreEngine } + +func (s *DefaultSqliteStore) GetLicense() string { + // TODO implement me + panic("implement me") +} + +func (s *DefaultSqliteStore) FindSettings(accountID string) (settings.Settings, error) { + account, ok := s.accounts[accountID] + if !ok { + return nil, status.Errorf(status.NotFound, "account not found") + } + return account.Settings, nil +} + +func (s *DefaultSqliteStore) FindPeerByPubKey(accountID string, pubKey string) (peers.Peer, error) { + a, ok := s.accounts[accountID] + if !ok { + return nil, status.Errorf(status.NotFound, "account not found") + } + for _, peer := range a.Peers { + if peer.Key == pubKey { + return peer.Copy(), nil + } + } + + return nil, status.Errorf(status.NotFound, "peer with the public key %s not found", pubKey) +} + +func (s *DefaultSqliteStore) FindPeerByID(accountID string, id string) (peers.Peer, error) { + a, ok := s.accounts[accountID] + if !ok { + return nil, status.Errorf(status.NotFound, "account not found") + } + for _, peer := range a.Peers { + if peer.ID == id { + return peer.Copy(), nil + } + } + + return nil, status.Errorf(status.NotFound, "peer with the ID %s not found", id) +} + +func (s *DefaultSqliteStore) FindAllPeersInAccount(accountId string) ([]peers.Peer, error) { + a, ok := s.accounts[accountID] + if !ok { + return nil, status.Errorf(status.NotFound, "account not found") + } + return a.Peers, nil +} + +func (s *DefaultSqliteStore) UpdatePeer(peer peers.Peer) error { + // TODO implement me + panic("implement me") +} diff --git a/management/refactor/store/store.go b/management/refactor/store/store.go index 747a326c6..f49372c95 100644 --- a/management/refactor/store/store.go +++ b/management/refactor/store/store.go @@ -7,27 +7,26 @@ import ( log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/management/refactor/peers" - "github.com/netbirdio/netbird/management/refactor/settings" + peerTypes "github.com/netbirdio/netbird/management/refactor/resources/peers/types" + settingsTypes "github.com/netbirdio/netbird/management/refactor/resources/settings/types" "github.com/netbirdio/netbird/management/server/telemetry" ) type Store interface { + AcquireAccountLock(id string) func() + AcquireGlobalLock() func() + LoadAccount(id string) error + WriteAccount(id string) error GetLicense() string - FindPeerByPubKey(pubKey string) (peers.Peer, error) - FindPeerByID(id string) (peers.Peer, error) - FindAllPeersInAccount(id string) ([]peers.Peer, error) - UpdatePeer(peer peers.Peer) error - FindSettings(accountID string) (settings.Settings, error) + FindPeerByPubKey(pubKey string) (peerTypes.Peer, error) + FindPeerByID(id string) (peerTypes.Peer, error) + FindAllPeersInAccount(id string) ([]peerTypes.Peer, error) + UpdatePeer(peer peerTypes.Peer) error + FindSettings(accountID string) (settingsTypes.Settings, error) } type DefaultStore interface { - GetLicense() string - FindPeerByPubKey(pubKey string) (peers.Peer, error) - FindPeerByID(id string) (peers.Peer, error) - FindAllPeersInAccount(id string) ([]peers.Peer, error) - UpdatePeer(peer peers.Peer) error - FindSettings(accountID string) (settings.Settings, error) + Store } type StoreEngine string diff --git a/management/refactor/users/manager.go b/management/refactor/users/manager.go deleted file mode 100644 index cce038aca..000000000 --- a/management/refactor/users/manager.go +++ /dev/null @@ -1,24 +0,0 @@ -package users - -import "github.com/netbirdio/netbird/management/refactor/peers" - -type Manager interface { - GetUser(id string) (User, error) -} - -type DefaultManager struct { - repository repository - peerManager peers.Manager -} - -func NewDefaultManager(repository repository, peerManager peers.Manager) *DefaultManager { - return &DefaultManager{ - repository: repository, - peerManager: peerManager, - } -} - -func (d DefaultManager) GetUser(id string) (User, error) { - // TODO implement me - panic("implement me") -}