diff --git a/dns/nameserver.go b/dns/nameserver.go index 4f9de57e7e..9f9cc8177f 100644 --- a/dns/nameserver.go +++ b/dns/nameserver.go @@ -127,6 +127,7 @@ func (g *NameServerGroup) Copy() *NameServerGroup { Description: g.Description, NameServers: g.NameServers, Groups: g.Groups, + Enabled: g.Enabled, } } diff --git a/management/server/http/api/cfg.yaml b/management/server/http/api/cfg.yaml index 5c0afab66e..b0ccd83215 100644 --- a/management/server/http/api/cfg.yaml +++ b/management/server/http/api/cfg.yaml @@ -3,3 +3,5 @@ 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/server/http/api/generate.sh b/management/server/http/api/generate.sh index d90db7a5f2..2f24fd9031 100644 --- a/management/server/http/api/generate.sh +++ b/management/server/http/api/generate.sh @@ -11,6 +11,6 @@ fi old_pwd=$(pwd) script_path=$(dirname $(realpath "$0")) cd "$script_path" -go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.11.0 +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/server/http/api/openapi.yml b/management/server/http/api/openapi.yml index 96f6c1972e..28a2419d2e 100644 --- a/management/server/http/api/openapi.yml +++ b/management/server/http/api/openapi.yml @@ -16,6 +16,8 @@ tags: description: Interact with and view information about rules. - name: Routes description: Interact with and view information about routes. + - name: DNS + description: Interact with and view information about DNS configuration. components: schemas: User: @@ -373,6 +375,76 @@ components: enum: [ "network","network_id","description","enabled","peer","metric","masquerade" ] required: - path + Nameserver: + type: object + properties: + ip: + description: Nameserver IP + type: string + ns_type: + description: Nameserver Type + type: string + enum: ["udp"] + port: + description: Nameserver Port + type: integer + required: + - ip + - ns_type + - port + NameserverGroupRequest: + type: object + properties: + name: + description: Nameserver group name + type: string + maxLength: 40 + minLength: 1 + description: + description: Nameserver group description + type: string + nameservers: + description: Nameserver group + minLength: 1 + maxLength: 2 + type: array + items: + $ref: '#/components/schemas/Nameserver' + enabled: + description: Nameserver group status + type: boolean + groups: + description: Nameserver group tag groups + type: array + items: + type: string + required: + - name + - description + - nameservers + - enabled + - groups + NameserverGroup: + allOf: + - type: object + properties: + id: + description: Nameserver group ID + type: string + required: + - id + - $ref: '#/components/schemas/NameserverGroupRequest' + NameserverGroupPatchOperation: + allOf: + - $ref: '#/components/schemas/PatchMinimum' + - type: object + properties: + path: + description: Nameserver group field to update in form / + type: string + enum: [ "name","description","enabled","groups","nameservers" ] + required: + - path responses: not_found: @@ -1238,6 +1310,176 @@ paths: schema: type: string description: The Route ID + 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: Returns a list of all Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + 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: Creates a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + 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/{id}: + get: + summary: Get information about a Nameserver Groups + tags: [ DNS ] + security: + - BearerAuth: [ ] + parameters: + - in: path + name: id + required: true + schema: + type: string + description: The Nameserver Group ID + 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/Replace a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + parameters: + - in: path + name: id + required: true + schema: + type: string + description: The Nameserver Group ID + 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" + patch: + summary: Update information about a Nameserver Group + tags: [ DNS ] + security: + - BearerAuth: [ ] + parameters: + - in: path + name: id + required: true + schema: + type: string + description: The Nameserver Group ID + requestBody: + description: Update Nameserver Group request using a list of json patch objects + content: + 'application/json': + schema: + type: array + items: + $ref: '#/components/schemas/NameserverGroupPatchOperation' + 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 + tags: [ DNS ] + security: + - BearerAuth: [ ] + parameters: + - in: path + name: id + required: true + schema: + type: string + description: The Nameserver Group ID responses: '200': description: Delete status code diff --git a/management/server/http/api/types.gen.go b/management/server/http/api/types.gen.go index 0ac2a81355..4e256666c9 100644 --- a/management/server/http/api/types.gen.go +++ b/management/server/http/api/types.gen.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/deepmap/oapi-codegen version v1.11.0 DO NOT EDIT. +// Code generated by github.com/deepmap/oapi-codegen version v1.11.1-0.20220912230023-4a1477f6a8ba DO NOT EDIT. package api import ( @@ -24,6 +24,27 @@ const ( GroupPatchOperationPathPeers GroupPatchOperationPath = "peers" ) +// Defines values for NameserverNsType. +const ( + NameserverNsTypeUdp NameserverNsType = "udp" +) + +// Defines values for NameserverGroupPatchOperationOp. +const ( + NameserverGroupPatchOperationOpAdd NameserverGroupPatchOperationOp = "add" + NameserverGroupPatchOperationOpRemove NameserverGroupPatchOperationOp = "remove" + NameserverGroupPatchOperationOpReplace NameserverGroupPatchOperationOp = "replace" +) + +// Defines values for NameserverGroupPatchOperationPath. +const ( + NameserverGroupPatchOperationPathDescription NameserverGroupPatchOperationPath = "description" + NameserverGroupPatchOperationPathEnabled NameserverGroupPatchOperationPath = "enabled" + NameserverGroupPatchOperationPathGroups NameserverGroupPatchOperationPath = "groups" + NameserverGroupPatchOperationPathName NameserverGroupPatchOperationPath = "name" + NameserverGroupPatchOperationPathNameservers NameserverGroupPatchOperationPath = "nameservers" +) + // Defines values for PatchMinimumOp. const ( PatchMinimumOpAdd PatchMinimumOp = "add" @@ -68,322 +89,397 @@ const ( // Group defines model for Group. type Group struct { - // Group ID + // Id Group ID Id string `json:"id"` - // Group Name identifier + // Name Group Name identifier Name string `json:"name"` - // List of peers object + // Peers List of peers object Peers []PeerMinimum `json:"peers"` - // Count of peers associated to the group + // PeersCount Count of peers associated to the group PeersCount int `json:"peers_count"` } // GroupMinimum defines model for GroupMinimum. type GroupMinimum struct { - // Group ID + // Id Group ID Id string `json:"id"` - // Group Name identifier + // Name Group Name identifier Name string `json:"name"` - // Count of peers associated to the group + // PeersCount Count of peers associated to the group PeersCount int `json:"peers_count"` } // GroupPatchOperation defines model for GroupPatchOperation. type GroupPatchOperation struct { - // Patch operation type + // Op Patch operation type Op GroupPatchOperationOp `json:"op"` - // Group field to update in form / + // Path Group field to update in form / Path GroupPatchOperationPath `json:"path"` - // Values to be applied + // Value Values to be applied Value []string `json:"value"` } -// Patch operation type +// GroupPatchOperationOp Patch operation type type GroupPatchOperationOp string -// Group field to update in form / +// GroupPatchOperationPath Group field to update in form / type GroupPatchOperationPath string +// Nameserver defines model for Nameserver. +type Nameserver struct { + // Ip Nameserver IP + Ip string `json:"ip"` + + // NsType Nameserver Type + NsType NameserverNsType `json:"ns_type"` + + // Port Nameserver Port + Port int `json:"port"` +} + +// NameserverNsType Nameserver Type +type NameserverNsType string + +// NameserverGroup defines model for NameserverGroup. +type NameserverGroup struct { + // Description Nameserver group description + Description string `json:"description"` + + // Enabled Nameserver group status + Enabled bool `json:"enabled"` + + // Groups Nameserver group tag groups + Groups []string `json:"groups"` + + // Id Nameserver group ID + Id string `json:"id"` + + // Name Nameserver group name + Name string `json:"name"` + + // Nameservers Nameserver group + Nameservers []Nameserver `json:"nameservers"` +} + +// NameserverGroupPatchOperation defines model for NameserverGroupPatchOperation. +type NameserverGroupPatchOperation struct { + // Op Patch operation type + Op NameserverGroupPatchOperationOp `json:"op"` + + // Path Nameserver group field to update in form / + Path NameserverGroupPatchOperationPath `json:"path"` + + // Value Values to be applied + Value []string `json:"value"` +} + +// NameserverGroupPatchOperationOp Patch operation type +type NameserverGroupPatchOperationOp string + +// NameserverGroupPatchOperationPath Nameserver group field to update in form / +type NameserverGroupPatchOperationPath string + +// NameserverGroupRequest defines model for NameserverGroupRequest. +type NameserverGroupRequest struct { + // Description Nameserver group description + Description string `json:"description"` + + // Enabled Nameserver group status + Enabled bool `json:"enabled"` + + // Groups Nameserver group tag groups + Groups []string `json:"groups"` + + // Name Nameserver group name + Name string `json:"name"` + + // Nameservers Nameserver group + Nameservers []Nameserver `json:"nameservers"` +} + // PatchMinimum defines model for PatchMinimum. type PatchMinimum struct { - // Patch operation type + // Op Patch operation type Op PatchMinimumOp `json:"op"` - // Values to be applied + // Value Values to be applied Value []string `json:"value"` } -// Patch operation type +// PatchMinimumOp Patch operation type type PatchMinimumOp string // Peer defines model for Peer. type Peer struct { - // Peer to Management connection status + // Connected Peer to Management connection status Connected bool `json:"connected"` - // Groups that the peer belongs to + // Groups Groups that the peer belongs to Groups []GroupMinimum `json:"groups"` - // Hostname of the machine + // Hostname Hostname of the machine Hostname string `json:"hostname"` - // Peer ID + // Id Peer ID Id string `json:"id"` - // Peer's IP address + // Ip Peer's IP address Ip string `json:"ip"` - // Last time peer connected to Netbird's management service + // LastSeen Last time peer connected to Netbird's management service LastSeen time.Time `json:"last_seen"` - // Peer's hostname + // Name Peer's hostname Name string `json:"name"` - // Peer's operating system and version + // Os Peer's operating system and version Os string `json:"os"` - // Indicates whether SSH server is enabled on this peer + // SshEnabled Indicates whether SSH server is enabled on this peer SshEnabled bool `json:"ssh_enabled"` - // Peer's desktop UI version + // UiVersion Peer's desktop UI version UiVersion *string `json:"ui_version,omitempty"` - // User ID of the user that enrolled this peer + // UserId User ID of the user that enrolled this peer UserId *string `json:"user_id,omitempty"` - // Peer's daemon or cli version + // Version Peer's daemon or cli version Version string `json:"version"` } // PeerMinimum defines model for PeerMinimum. type PeerMinimum struct { - // Peer ID + // Id Peer ID Id string `json:"id"` - // Peer's hostname + // Name Peer's hostname Name string `json:"name"` } // Route defines model for Route. type Route struct { - // Route description + // Description Route description Description string `json:"description"` - // Route status + // Enabled Route status Enabled bool `json:"enabled"` - // Route Id + // Id Route Id Id string `json:"id"` - // Indicate if peer should masquerade traffic to this route's prefix + // Masquerade Indicate if peer should masquerade traffic to this route's prefix Masquerade bool `json:"masquerade"` - // Route metric number. Lowest number has higher priority + // Metric Route metric number. Lowest number has higher priority Metric int `json:"metric"` - // Network range in CIDR format + // Network Network range in CIDR format Network string `json:"network"` - // Route network identifier, to group HA routes + // NetworkId Route network identifier, to group HA routes NetworkId string `json:"network_id"` - // Network type indicating if it is IPv4 or IPv6 + // NetworkType Network type indicating if it is IPv4 or IPv6 NetworkType string `json:"network_type"` - // Peer Identifier associated with route + // Peer Peer Identifier associated with route Peer string `json:"peer"` } // RoutePatchOperation defines model for RoutePatchOperation. type RoutePatchOperation struct { - // Patch operation type + // Op Patch operation type Op RoutePatchOperationOp `json:"op"` - // Route field to update in form / + // Path Route field to update in form / Path RoutePatchOperationPath `json:"path"` - // Values to be applied + // Value Values to be applied Value []string `json:"value"` } -// Patch operation type +// RoutePatchOperationOp Patch operation type type RoutePatchOperationOp string -// Route field to update in form / +// RoutePatchOperationPath Route field to update in form / type RoutePatchOperationPath string // RouteRequest defines model for RouteRequest. type RouteRequest struct { - // Route description + // Description Route description Description string `json:"description"` - // Route status + // Enabled Route status Enabled bool `json:"enabled"` - // Indicate if peer should masquerade traffic to this route's prefix + // Masquerade Indicate if peer should masquerade traffic to this route's prefix Masquerade bool `json:"masquerade"` - // Route metric number. Lowest number has higher priority + // Metric Route metric number. Lowest number has higher priority Metric int `json:"metric"` - // Network range in CIDR format + // Network Network range in CIDR format Network string `json:"network"` - // Route network identifier, to group HA routes + // NetworkId Route network identifier, to group HA routes NetworkId string `json:"network_id"` - // Peer Identifier associated with route + // Peer Peer Identifier associated with route Peer string `json:"peer"` } // Rule defines model for Rule. type Rule struct { - // Rule friendly description + // Description Rule friendly description Description string `json:"description"` - // Rule destination groups + // Destinations Rule destination groups Destinations []GroupMinimum `json:"destinations"` - // Rules status + // Disabled Rules status Disabled bool `json:"disabled"` - // Rule flow, currently, only "bidirect" for bi-directional traffic is accepted + // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted Flow string `json:"flow"` - // Rule ID + // Id Rule ID Id string `json:"id"` - // Rule name identifier + // Name Rule name identifier Name string `json:"name"` - // Rule source groups + // Sources Rule source groups Sources []GroupMinimum `json:"sources"` } // RuleMinimum defines model for RuleMinimum. type RuleMinimum struct { - // Rule friendly description + // Description Rule friendly description Description string `json:"description"` - // Rules status + // Disabled Rules status Disabled bool `json:"disabled"` - // Rule flow, currently, only "bidirect" for bi-directional traffic is accepted + // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted Flow string `json:"flow"` - // Rule name identifier + // Name Rule name identifier Name string `json:"name"` } // RulePatchOperation defines model for RulePatchOperation. type RulePatchOperation struct { - // Patch operation type + // Op Patch operation type Op RulePatchOperationOp `json:"op"` - // Rule field to update in form / + // Path Rule field to update in form / Path RulePatchOperationPath `json:"path"` - // Values to be applied + // Value Values to be applied Value []string `json:"value"` } -// Patch operation type +// RulePatchOperationOp Patch operation type type RulePatchOperationOp string -// Rule field to update in form / +// RulePatchOperationPath Rule field to update in form / type RulePatchOperationPath string // SetupKey defines model for SetupKey. type SetupKey struct { - // Setup key groups to auto-assign to peers registered with this key + // AutoGroups Setup key groups to auto-assign to peers registered with this key AutoGroups []string `json:"auto_groups"` - // Setup Key expiration date + // Expires Setup Key expiration date Expires time.Time `json:"expires"` - // Setup Key ID + // Id Setup Key ID Id string `json:"id"` - // Setup Key value + // Key Setup Key value Key string `json:"key"` - // Setup key last usage date + // LastUsed Setup key last usage date LastUsed time.Time `json:"last_used"` - // Setup key name identifier + // Name Setup key name identifier Name string `json:"name"` - // Setup key revocation status + // Revoked Setup key revocation status Revoked bool `json:"revoked"` - // Setup key status, "valid", "overused","expired" or "revoked" + // State Setup key status, "valid", "overused","expired" or "revoked" State string `json:"state"` - // Setup key type, one-off for single time usage and reusable + // Type Setup key type, one-off for single time usage and reusable Type string `json:"type"` - // Setup key last update date + // UpdatedAt Setup key last update date UpdatedAt time.Time `json:"updated_at"` - // Usage count of setup key + // UsedTimes Usage count of setup key UsedTimes int `json:"used_times"` - // Setup key validity status + // Valid Setup key validity status Valid bool `json:"valid"` } // SetupKeyRequest defines model for SetupKeyRequest. type SetupKeyRequest struct { - // Setup key groups to auto-assign to peers registered with this key + // AutoGroups Setup key groups to auto-assign to peers registered with this key AutoGroups []string `json:"auto_groups"` - // Expiration time in seconds + // ExpiresIn Expiration time in seconds ExpiresIn int `json:"expires_in"` - // Setup Key name + // Name Setup Key name Name string `json:"name"` - // Setup key revocation status + // Revoked Setup key revocation status Revoked bool `json:"revoked"` - // Setup key type, one-off for single time usage and reusable + // Type Setup key type, one-off for single time usage and reusable Type string `json:"type"` } // User defines model for User. type User struct { - // Groups to auto-assign to peers registered by this user + // AutoGroups Groups to auto-assign to peers registered by this user AutoGroups []string `json:"auto_groups"` - // User's email address + // Email User's email address Email string `json:"email"` - // User ID + // Id User ID Id string `json:"id"` - // User's name from idp provider + // Name User's name from idp provider Name string `json:"name"` - // User's NetBird account role + // Role User's NetBird account role Role string `json:"role"` } // UserRequest defines model for UserRequest. type UserRequest struct { - // Groups to auto-assign to peers registered by this user + // AutoGroups Groups to auto-assign to peers registered by this user AutoGroups []string `json:"auto_groups"` - // User's NetBird account role + // Role User's NetBird account role Role string `json:"role"` } +// PatchApiDnsNameserversIdJSONBody defines parameters for PatchApiDnsNameserversId. +type PatchApiDnsNameserversIdJSONBody = []NameserverGroupPatchOperation + // PostApiGroupsJSONBody defines parameters for PostApiGroups. type PostApiGroupsJSONBody struct { Name string `json:"name"` @@ -405,28 +501,22 @@ type PutApiPeersIdJSONBody struct { SshEnabled bool `json:"ssh_enabled"` } -// PostApiRoutesJSONBody defines parameters for PostApiRoutes. -type PostApiRoutesJSONBody = RouteRequest - // PatchApiRoutesIdJSONBody defines parameters for PatchApiRoutesId. type PatchApiRoutesIdJSONBody = []RoutePatchOperation -// PutApiRoutesIdJSONBody defines parameters for PutApiRoutesId. -type PutApiRoutesIdJSONBody = RouteRequest - // PostApiRulesJSONBody defines parameters for PostApiRules. type PostApiRulesJSONBody struct { - // Rule friendly description + // Description Rule friendly description Description string `json:"description"` Destinations *[]string `json:"destinations,omitempty"` - // Rules status + // Disabled Rules status Disabled bool `json:"disabled"` - // Rule flow, currently, only "bidirect" for bi-directional traffic is accepted + // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted Flow string `json:"flow"` - // Rule name identifier + // Name Rule name identifier Name string `json:"name"` Sources *[]string `json:"sources,omitempty"` } @@ -436,29 +526,29 @@ type PatchApiRulesIdJSONBody = []RulePatchOperation // PutApiRulesIdJSONBody defines parameters for PutApiRulesId. type PutApiRulesIdJSONBody struct { - // Rule friendly description + // Description Rule friendly description Description string `json:"description"` Destinations *[]string `json:"destinations,omitempty"` - // Rules status + // Disabled Rules status Disabled bool `json:"disabled"` - // Rule flow, currently, only "bidirect" for bi-directional traffic is accepted + // Flow Rule flow, currently, only "bidirect" for bi-directional traffic is accepted Flow string `json:"flow"` - // Rule name identifier + // Name Rule name identifier Name string `json:"name"` Sources *[]string `json:"sources,omitempty"` } -// PostApiSetupKeysJSONBody defines parameters for PostApiSetupKeys. -type PostApiSetupKeysJSONBody = SetupKeyRequest +// PostApiDnsNameserversJSONRequestBody defines body for PostApiDnsNameservers for application/json ContentType. +type PostApiDnsNameserversJSONRequestBody = NameserverGroupRequest -// PutApiSetupKeysIdJSONBody defines parameters for PutApiSetupKeysId. -type PutApiSetupKeysIdJSONBody = SetupKeyRequest +// PatchApiDnsNameserversIdJSONRequestBody defines body for PatchApiDnsNameserversId for application/json ContentType. +type PatchApiDnsNameserversIdJSONRequestBody = PatchApiDnsNameserversIdJSONBody -// PutApiUsersIdJSONBody defines parameters for PutApiUsersId. -type PutApiUsersIdJSONBody = UserRequest +// PutApiDnsNameserversIdJSONRequestBody defines body for PutApiDnsNameserversId for application/json ContentType. +type PutApiDnsNameserversIdJSONRequestBody = NameserverGroupRequest // PostApiGroupsJSONRequestBody defines body for PostApiGroups for application/json ContentType. type PostApiGroupsJSONRequestBody PostApiGroupsJSONBody @@ -473,13 +563,13 @@ type PutApiGroupsIdJSONRequestBody PutApiGroupsIdJSONBody type PutApiPeersIdJSONRequestBody PutApiPeersIdJSONBody // PostApiRoutesJSONRequestBody defines body for PostApiRoutes for application/json ContentType. -type PostApiRoutesJSONRequestBody = PostApiRoutesJSONBody +type PostApiRoutesJSONRequestBody = RouteRequest // PatchApiRoutesIdJSONRequestBody defines body for PatchApiRoutesId for application/json ContentType. type PatchApiRoutesIdJSONRequestBody = PatchApiRoutesIdJSONBody // PutApiRoutesIdJSONRequestBody defines body for PutApiRoutesId for application/json ContentType. -type PutApiRoutesIdJSONRequestBody = PutApiRoutesIdJSONBody +type PutApiRoutesIdJSONRequestBody = RouteRequest // PostApiRulesJSONRequestBody defines body for PostApiRules for application/json ContentType. type PostApiRulesJSONRequestBody PostApiRulesJSONBody @@ -491,10 +581,10 @@ type PatchApiRulesIdJSONRequestBody = PatchApiRulesIdJSONBody type PutApiRulesIdJSONRequestBody PutApiRulesIdJSONBody // PostApiSetupKeysJSONRequestBody defines body for PostApiSetupKeys for application/json ContentType. -type PostApiSetupKeysJSONRequestBody = PostApiSetupKeysJSONBody +type PostApiSetupKeysJSONRequestBody = SetupKeyRequest // PutApiSetupKeysIdJSONRequestBody defines body for PutApiSetupKeysId for application/json ContentType. -type PutApiSetupKeysIdJSONRequestBody = PutApiSetupKeysIdJSONBody +type PutApiSetupKeysIdJSONRequestBody = SetupKeyRequest // PutApiUsersIdJSONRequestBody defines body for PutApiUsersId for application/json ContentType. -type PutApiUsersIdJSONRequestBody = PutApiUsersIdJSONBody +type PutApiUsersIdJSONRequestBody = UserRequest diff --git a/management/server/http/handler.go b/management/server/http/handler.go index 1a61fd04e2..a4aeb82769 100644 --- a/management/server/http/handler.go +++ b/management/server/http/handler.go @@ -34,6 +34,7 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience keysHandler := NewSetupKeysHandler(accountManager, authAudience) userHandler := NewUserHandler(accountManager, authAudience) routesHandler := NewRoutes(accountManager, authAudience) + nameserversHandler := NewNameservers(accountManager, authAudience) apiHandler.HandleFunc("/api/peers", peersHandler.GetPeers).Methods("GET", "OPTIONS") apiHandler.HandleFunc("/api/peers/{id}", peersHandler.HandlePeer). @@ -67,6 +68,13 @@ func APIHandler(accountManager s.AccountManager, authIssuer string, authAudience apiHandler.HandleFunc("/api/routes/{id}", routesHandler.GetRouteHandler).Methods("GET", "OPTIONS") apiHandler.HandleFunc("/api/routes/{id}", routesHandler.DeleteRouteHandler).Methods("DELETE", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers", nameserversHandler.GetAllNameserversHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers", nameserversHandler.CreateNameserverGroupHandler).Methods("POST", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers/{id}", nameserversHandler.UpdateNameserverGroupHandler).Methods("PUT", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers/{id}", nameserversHandler.PatchNameserverGroupHandler).Methods("PATCH", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers/{id}", nameserversHandler.GetNameserverGroupHandler).Methods("GET", "OPTIONS") + apiHandler.HandleFunc("/api/dns/nameservers/{id}", nameserversHandler.DeleteNameserverGroupHandler).Methods("DELETE", "OPTIONS") + return apiHandler, nil } diff --git a/management/server/http/nameservers.go b/management/server/http/nameservers.go new file mode 100644 index 0000000000..af8bb08a45 --- /dev/null +++ b/management/server/http/nameservers.go @@ -0,0 +1,286 @@ +package http + +import ( + "encoding/json" + "fmt" + "github.com/gorilla/mux" + nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server" + "github.com/netbirdio/netbird/management/server/http/api" + "github.com/netbirdio/netbird/management/server/jwtclaims" + log "github.com/sirupsen/logrus" + "net/http" +) + +// Nameservers is the nameserver group handler of the account +type Nameservers struct { + jwtExtractor jwtclaims.ClaimsExtractor + accountManager server.AccountManager + authAudience string +} + +// NewNameservers returns a new instance of Nameservers handler +func NewNameservers(accountManager server.AccountManager, authAudience string) *Nameservers { + return &Nameservers{ + accountManager: accountManager, + authAudience: authAudience, + jwtExtractor: *jwtclaims.NewClaimsExtractor(nil), + } +} + +// GetAllNameserversHandler returns the list of nameserver groups for the account +func (h *Nameservers) GetAllNameserversHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + + nsGroups, err := h.accountManager.ListNameServerGroups(account.Id) + if err != nil { + toHTTPError(err, w) + return + } + + apiNameservers := make([]*api.NameserverGroup, 0) + for _, r := range nsGroups { + apiNameservers = append(apiNameservers, toNameserverGroupResponse(r)) + } + + writeJSONObject(w, apiNameservers) +} + +// CreateNameserverGroupHandler handles nameserver group creation request +func (h *Nameservers) CreateNameserverGroupHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + var req api.PostApiDnsNameserversJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + nsList, err := toServerNSList(req.Nameservers) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + nsGroup, err := h.accountManager.CreateNameServerGroup(account.Id, req.Name, req.Description, nsList, req.Groups, req.Enabled) + if err != nil { + toHTTPError(err, w) + return + } + + resp := toNameserverGroupResponse(nsGroup) + + writeJSONObject(w, &resp) +} + +// UpdateNameserverGroupHandler handles update to a nameserver group identified by a given ID +func (h *Nameservers) UpdateNameserverGroupHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + + nsGroupID := mux.Vars(r)["id"] + if len(nsGroupID) == 0 { + http.Error(w, "invalid nameserver group ID", http.StatusBadRequest) + return + } + + var req api.PutApiDnsNameserversIdJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + nsList, err := toServerNSList(req.Nameservers) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + updatedNSGroup := &nbdns.NameServerGroup{ + ID: nsGroupID, + Name: req.Name, + Description: req.Description, + NameServers: nsList, + Groups: req.Groups, + Enabled: req.Enabled, + } + + err = h.accountManager.SaveNameServerGroup(account.Id, updatedNSGroup) + if err != nil { + toHTTPError(err, w) + return + } + + resp := toNameserverGroupResponse(updatedNSGroup) + + writeJSONObject(w, &resp) +} + +// PatchNameserverGroupHandler handles patch updates to a nameserver group identified by a given ID +func (h *Nameservers) PatchNameserverGroupHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + + nsGroupID := mux.Vars(r)["id"] + if len(nsGroupID) == 0 { + http.Error(w, "invalid nameserver group ID", http.StatusBadRequest) + return + } + + var req api.PatchApiDnsNameserversIdJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + var operations []server.NameServerGroupUpdateOperation + for _, patch := range req { + if patch.Op != api.NameserverGroupPatchOperationOpReplace { + http.Error(w, fmt.Sprintf("nameserver groups only accepts replace operations, got %s", patch.Op), + http.StatusBadRequest) + return + } + switch patch.Path { + case api.NameserverGroupPatchOperationPathName: + operations = append(operations, server.NameServerGroupUpdateOperation{ + Type: server.UpdateNameServerGroupName, + Values: patch.Value, + }) + case api.NameserverGroupPatchOperationPathDescription: + operations = append(operations, server.NameServerGroupUpdateOperation{ + Type: server.UpdateNameServerGroupDescription, + Values: patch.Value, + }) + case api.NameserverGroupPatchOperationPathNameservers: + operations = append(operations, server.NameServerGroupUpdateOperation{ + Type: server.UpdateNameServerGroupNameServers, + Values: patch.Value, + }) + case api.NameserverGroupPatchOperationPathGroups: + operations = append(operations, server.NameServerGroupUpdateOperation{ + Type: server.UpdateNameServerGroupGroups, + Values: patch.Value, + }) + case api.NameserverGroupPatchOperationPathEnabled: + operations = append(operations, server.NameServerGroupUpdateOperation{ + Type: server.UpdateNameServerGroupEnabled, + Values: patch.Value, + }) + default: + http.Error(w, "invalid patch path", http.StatusBadRequest) + return + } + } + + updatedNSGroup, err := h.accountManager.UpdateNameServerGroup(account.Id, nsGroupID, operations) + if err != nil { + toHTTPError(err, w) + return + } + + resp := toNameserverGroupResponse(updatedNSGroup) + + writeJSONObject(w, &resp) +} + +// DeleteNameserverGroupHandler handles nameserver group deletion request +func (h *Nameservers) DeleteNameserverGroupHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + + nsGroupID := mux.Vars(r)["id"] + if len(nsGroupID) == 0 { + http.Error(w, "invalid nameserver group ID", http.StatusBadRequest) + return + } + + err = h.accountManager.DeleteNameServerGroup(account.Id, nsGroupID) + if err != nil { + toHTTPError(err, w) + return + } + + writeJSONObject(w, "") +} + +// GetNameserverGroupHandler handles a nameserver group Get request identified by ID +func (h *Nameservers) GetNameserverGroupHandler(w http.ResponseWriter, r *http.Request) { + account, err := getJWTAccount(h.accountManager, h.jwtExtractor, h.authAudience, r) + if err != nil { + log.Error(err) + http.Redirect(w, r, "/", http.StatusInternalServerError) + return + } + + nsGroupID := mux.Vars(r)["id"] + if len(nsGroupID) == 0 { + http.Error(w, "invalid nameserver group ID", http.StatusBadRequest) + return + } + + nsGroup, err := h.accountManager.GetNameServerGroup(account.Id, nsGroupID) + if err != nil { + toHTTPError(err, w) + return + } + + resp := toNameserverGroupResponse(nsGroup) + + writeJSONObject(w, &resp) + +} + +func toServerNSList(apiNSList []api.Nameserver) ([]nbdns.NameServer, error) { + var nsList []nbdns.NameServer + for _, apiNS := range apiNSList { + parsed, err := nbdns.ParseNameServerURL(fmt.Sprintf("%s://%s:%d", apiNS.NsType, apiNS.Ip, apiNS.Port)) + if err != nil { + return nil, err + } + nsList = append(nsList, parsed) + } + + return nsList, nil +} + +func toNameserverGroupResponse(serverNSGroup *nbdns.NameServerGroup) *api.NameserverGroup { + var nsList []api.Nameserver + for _, ns := range serverNSGroup.NameServers { + apiNS := api.Nameserver{ + Ip: ns.IP.String(), + NsType: api.NameserverNsType(ns.NSType.String()), + Port: ns.Port, + } + nsList = append(nsList, apiNS) + } + + return &api.NameserverGroup{ + Id: serverNSGroup.ID, + Name: serverNSGroup.Name, + Description: serverNSGroup.Description, + Groups: serverNSGroup.Groups, + Nameservers: nsList, + Enabled: serverNSGroup.Enabled, + } +} diff --git a/management/server/http/nameservers_test.go b/management/server/http/nameservers_test.go new file mode 100644 index 0000000000..06438415d3 --- /dev/null +++ b/management/server/http/nameservers_test.go @@ -0,0 +1,287 @@ +package http + +import ( + "bytes" + "encoding/json" + nbdns "github.com/netbirdio/netbird/dns" + "github.com/netbirdio/netbird/management/server/http/api" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "io" + "net/http" + "net/http/httptest" + "net/netip" + "testing" + + "github.com/gorilla/mux" + "github.com/netbirdio/netbird/management/server" + "github.com/netbirdio/netbird/management/server/jwtclaims" + "github.com/netbirdio/netbird/management/server/mock_server" +) + +const ( + existingNSGroupID = "existingNSGroupID" + notFoundNSGroupID = "notFoundNSGroupID" + testNSGroupAccountID = "test_id" +) + +var testingNSAccount = &server.Account{ + Id: testNSGroupAccountID, + Domain: "hotmail.com", +} + +var baseExistingNSGroup = &nbdns.NameServerGroup{ + ID: existingNSGroupID, + Name: "super", + Description: "super", + NameServers: []nbdns.NameServer{ + { + IP: netip.MustParseAddr("1.1.1.1"), + NSType: nbdns.UDPNameServerType, + Port: nbdns.DefaultDNSPort, + }, + { + IP: netip.MustParseAddr("1.1.2.2"), + NSType: nbdns.UDPNameServerType, + Port: nbdns.DefaultDNSPort, + }, + }, + Groups: []string{"testing"}, + Enabled: true, +} + +func initNameserversTestData() *Nameservers { + return &Nameservers{ + accountManager: &mock_server.MockAccountManager{ + GetNameServerGroupFunc: func(accountID, nsGroupID string) (*nbdns.NameServerGroup, error) { + if nsGroupID == existingNSGroupID { + return baseExistingNSGroup.Copy(), nil + } + return nil, status.Errorf(codes.NotFound, "nameserver group with ID %s not found", nsGroupID) + }, + CreateNameServerGroupFunc: func(accountID string, name, description string, nameServerList []nbdns.NameServer, groups []string, enabled bool) (*nbdns.NameServerGroup, error) { + return &nbdns.NameServerGroup{ + ID: existingNSGroupID, + Name: name, + Description: description, + NameServers: nameServerList, + Groups: groups, + Enabled: enabled, + }, nil + }, + DeleteNameServerGroupFunc: func(accountID, nsGroupID string) error { + return nil + }, + SaveNameServerGroupFunc: func(accountID string, nsGroupToSave *nbdns.NameServerGroup) error { + if nsGroupToSave.ID == existingNSGroupID { + return nil + } + return status.Errorf(codes.NotFound, "nameserver group with ID %s was not found", nsGroupToSave.ID) + }, + UpdateNameServerGroupFunc: func(accountID, nsGroupID string, operations []server.NameServerGroupUpdateOperation) (*nbdns.NameServerGroup, error) { + nsGroupToUpdate := baseExistingNSGroup.Copy() + if nsGroupID != nsGroupToUpdate.ID { + return nil, status.Errorf(codes.NotFound, "nameserver group ID %s no longer exists", nsGroupID) + } + for _, operation := range operations { + switch operation.Type { + case server.UpdateNameServerGroupName: + nsGroupToUpdate.Name = operation.Values[0] + case server.UpdateNameServerGroupDescription: + nsGroupToUpdate.Description = operation.Values[0] + case server.UpdateNameServerGroupNameServers: + var parsedNSList []nbdns.NameServer + for _, nsURL := range operation.Values { + parsed, err := nbdns.ParseNameServerURL(nsURL) + if err != nil { + return nil, err + } + parsedNSList = append(parsedNSList, parsed) + } + nsGroupToUpdate.NameServers = parsedNSList + } + } + return nsGroupToUpdate, nil + }, + GetAccountWithAuthorizationClaimsFunc: func(_ jwtclaims.AuthorizationClaims) (*server.Account, error) { + return testingNSAccount, nil + }, + }, + authAudience: "", + jwtExtractor: jwtclaims.ClaimsExtractor{ + ExtractClaimsFromRequestContext: func(r *http.Request, authAudiance string) jwtclaims.AuthorizationClaims { + return jwtclaims.AuthorizationClaims{ + UserId: "test_user", + Domain: "hotmail.com", + AccountId: testNSGroupAccountID, + } + }, + }, + } +} + +func TestNameserversHandlers(t *testing.T) { + tt := []struct { + name string + expectedStatus int + expectedBody bool + expectedNSGroup *api.NameserverGroup + requestType string + requestPath string + requestBody io.Reader + }{ + { + name: "Get Existing Nameserver Group", + requestType: http.MethodGet, + requestPath: "/api/dns/nameservers/" + existingNSGroupID, + expectedStatus: http.StatusOK, + expectedBody: true, + expectedNSGroup: toNameserverGroupResponse(baseExistingNSGroup), + }, + { + name: "Get Not Existing Nameserver Group", + requestType: http.MethodGet, + requestPath: "/api/dns/nameservers/" + notFoundNSGroupID, + expectedStatus: http.StatusNotFound, + }, + { + name: "POST OK", + requestType: http.MethodPost, + requestPath: "/api/dns/nameservers", + requestBody: bytes.NewBuffer( + []byte("{\"name\":\"name\",\"Description\":\"Post\",\"nameservers\":[{\"ip\":\"1.1.1.1\",\"ns_type\":\"udp\",\"port\":53}],\"groups\":[\"group\"],\"enabled\":true}")), + expectedStatus: http.StatusOK, + expectedBody: true, + expectedNSGroup: &api.NameserverGroup{ + Id: existingNSGroupID, + Name: "name", + Description: "Post", + Nameservers: []api.Nameserver{ + { + Ip: "1.1.1.1", + NsType: "udp", + Port: 53, + }, + }, + Groups: []string{"group"}, + Enabled: true, + }, + }, + { + name: "POST Invalid Nameserver", + requestType: http.MethodPost, + requestPath: "/api/dns/nameservers", + requestBody: bytes.NewBuffer( + []byte("{\"name\":\"name\",\"Description\":\"Post\",\"nameservers\":[{\"ip\":\"1000\",\"ns_type\":\"udp\",\"port\":53}],\"groups\":[\"group\"],\"enabled\":true}")), + expectedStatus: http.StatusBadRequest, + expectedBody: false, + }, + { + name: "PUT OK", + requestType: http.MethodPut, + requestPath: "/api/dns/nameservers/" + existingNSGroupID, + requestBody: bytes.NewBuffer( + []byte("{\"name\":\"name\",\"Description\":\"Post\",\"nameservers\":[{\"ip\":\"1.1.1.1\",\"ns_type\":\"udp\",\"port\":53}],\"groups\":[\"group\"],\"enabled\":true}")), + expectedStatus: http.StatusOK, + expectedBody: true, + expectedNSGroup: &api.NameserverGroup{ + Id: existingNSGroupID, + Name: "name", + Description: "Post", + Nameservers: []api.Nameserver{ + { + Ip: "1.1.1.1", + NsType: "udp", + Port: 53, + }, + }, + Groups: []string{"group"}, + Enabled: true, + }, + }, + { + name: "PUT Not Existing Nameserver Group", + requestType: http.MethodPut, + requestPath: "/api/dns/nameservers/" + notFoundNSGroupID, + requestBody: bytes.NewBuffer( + []byte("{\"name\":\"name\",\"Description\":\"Post\",\"nameservers\":[{\"ip\":\"1.1.1.1\",\"ns_type\":\"udp\",\"port\":53}],\"groups\":[\"group\"],\"enabled\":true}")), + expectedStatus: http.StatusNotFound, + expectedBody: false, + }, + { + name: "PUT Invalid Nameserver", + requestType: http.MethodPut, + requestPath: "/api/dns/nameservers/" + notFoundNSGroupID, + requestBody: bytes.NewBuffer( + []byte("{\"name\":\"name\",\"Description\":\"Post\",\"nameservers\":[{\"ip\":\"100\",\"ns_type\":\"udp\",\"port\":53}],\"groups\":[\"group\"],\"enabled\":true}")), + expectedStatus: http.StatusBadRequest, + expectedBody: false, + }, + { + name: "PATCH OK", + requestType: http.MethodPatch, + requestPath: "/api/dns/nameservers/" + existingNSGroupID, + requestBody: bytes.NewBufferString("[{\"op\":\"replace\",\"path\":\"description\",\"value\":[\"NewDesc\"]}]"), + expectedStatus: http.StatusOK, + expectedBody: true, + expectedNSGroup: &api.NameserverGroup{ + Id: existingNSGroupID, + Name: baseExistingNSGroup.Name, + Description: "NewDesc", + Nameservers: toNameserverGroupResponse(baseExistingNSGroup).Nameservers, + Groups: baseExistingNSGroup.Groups, + Enabled: baseExistingNSGroup.Enabled, + }, + }, + { + name: "PATCH Invalid Nameserver Group OK", + requestType: http.MethodPatch, + requestPath: "/api/dns/nameservers/" + notFoundRouteID, + requestBody: bytes.NewBufferString("[{\"op\":\"replace\",\"path\":\"description\",\"value\":[\"NewDesc\"]}]"), + expectedStatus: http.StatusNotFound, + expectedBody: false, + }, + } + + p := initNameserversTestData() + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + recorder := httptest.NewRecorder() + req := httptest.NewRequest(tc.requestType, tc.requestPath, tc.requestBody) + + router := mux.NewRouter() + router.HandleFunc("/api/dns/nameservers/{id}", p.GetNameserverGroupHandler).Methods("GET") + router.HandleFunc("/api/dns/nameservers", p.CreateNameserverGroupHandler).Methods("POST") + router.HandleFunc("/api/dns/nameservers/{id}", p.DeleteNameserverGroupHandler).Methods("DELETE") + router.HandleFunc("/api/dns/nameservers/{id}", p.UpdateNameserverGroupHandler).Methods("PUT") + router.HandleFunc("/api/dns/nameservers/{id}", p.PatchNameserverGroupHandler).Methods("PATCH") + router.ServeHTTP(recorder, req) + + res := recorder.Result() + defer res.Body.Close() + + content, err := io.ReadAll(res.Body) + if err != nil { + t.Fatalf("I don't know what I expected; %v", err) + } + + if status := recorder.Code; status != tc.expectedStatus { + t.Errorf("handler returned wrong status code: got %v want %v, content: %s", + status, tc.expectedStatus, string(content)) + return + } + + if !tc.expectedBody { + return + } + + got := &api.NameserverGroup{} + if err = json.Unmarshal(content, &got); err != nil { + t.Fatalf("Sent content is not in correct json format; %v", err) + } + assert.Equal(t, tc.expectedNSGroup, got) + }) + } +} diff --git a/management/server/http/routes.go b/management/server/http/routes.go index 7a26ae2e7c..1d8fa15032 100644 --- a/management/server/http/routes.go +++ b/management/server/http/routes.go @@ -123,7 +123,7 @@ func (h *Routes) UpdateRouteHandler(w http.ResponseWriter, r *http.Request) { return } - var req api.PutApiRoutesIdJSONBody + var req api.PutApiRoutesIdJSONRequestBody if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/management/server/http/util.go b/management/server/http/util.go index 9d14b2b529..4e13b0e58e 100644 --- a/management/server/http/util.go +++ b/management/server/http/util.go @@ -6,11 +6,14 @@ import ( "fmt" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/jwtclaims" + log "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "net/http" "time" ) -//writeJSONObject simply writes object to the HTTP reponse in JSON format +// writeJSONObject simply writes object to the HTTP reponse in JSON format func writeJSONObject(w http.ResponseWriter, obj interface{}) { w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json; charset=UTF-8") @@ -21,7 +24,7 @@ func writeJSONObject(w http.ResponseWriter, obj interface{}) { } } -//Duration is used strictly for JSON requests/responses due to duration marshalling issues +// Duration is used strictly for JSON requests/responses due to duration marshalling issues type Duration struct { time.Duration } @@ -64,3 +67,25 @@ func getJWTAccount(accountManager server.AccountManager, return account, nil } + +func toHTTPError(err error, w http.ResponseWriter) { + errStatus, ok := status.FromError(err) + if ok && errStatus.Code() == codes.Internal { + http.Error(w, errStatus.String(), http.StatusInternalServerError) + return + } + + if ok && errStatus.Code() == codes.NotFound { + http.Error(w, errStatus.String(), http.StatusNotFound) + return + } + + if ok && errStatus.Code() == codes.InvalidArgument { + http.Error(w, errStatus.String(), http.StatusBadRequest) + return + } + + unhandledMSG := fmt.Sprintf("got unhandled error code, error: %s", errStatus.String()) + log.Error(unhandledMSG) + http.Error(w, unhandledMSG, http.StatusInternalServerError) +}