From 6fb69c59d25920a0e3b685a375161ef3abd07e9e Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Mon, 9 Dec 2024 14:21:49 -0500 Subject: [PATCH] list members of organization handler (#537) --- controller/controller.go | 1 + controller/listFrontends.go | 2 +- controller/listOrganizationMembers.go | 50 ++++++++ controller/store/organizationMember.go | 4 + rest_client_zrok/admin/admin_client.go | 2 +- .../list_organization_members_responses.go | 76 ++++++++++-- rest_server_zrok/embedded_spec.go | 10 +- .../admin/list_organization_members.go | 2 +- .../list_organization_members_responses.go | 25 ++++ .../list_organization_members_urlbuilder.go | 2 +- rest_server_zrok/operations/zrok_api.go | 2 +- sdk/nodejs/sdk/src/zrok/api/api/adminApi.ts | 2 +- sdk/python/sdk/zrok/zrok_api/__init__.py | 2 +- sdk/python/sdk/zrok/zrok_api/api/admin_api.py | 6 +- .../sdk/zrok/zrok_api/models/__init__.py | 2 +- .../zrok_api/models/organization_list_body.py | 110 ++++++++++++++++++ specs/zrok.yml | 4 +- ui/src/api/admin.js | 2 +- 18 files changed, 282 insertions(+), 22 deletions(-) create mode 100644 controller/listOrganizationMembers.go create mode 100644 sdk/python/sdk/zrok/zrok_api/models/organization_list_body.py diff --git a/controller/controller.go b/controller/controller.go index 852dc08a..584703db 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -60,6 +60,7 @@ func Run(inCfg *config.Config) error { api.AdminGrantsHandler = newGrantsHandler() api.AdminInviteTokenGenerateHandler = newInviteTokenGenerateHandler() api.AdminListFrontendsHandler = newListFrontendsHandler() + api.AdminListOrganizationMembersHandler = newListOrganizationMembersHandler() api.AdminUpdateFrontendHandler = newUpdateFrontendHandler() api.EnvironmentEnableHandler = newEnableHandler() api.EnvironmentDisableHandler = newDisableHandler() diff --git a/controller/listFrontends.go b/controller/listFrontends.go index 640b340d..00623ee8 100644 --- a/controller/listFrontends.go +++ b/controller/listFrontends.go @@ -15,7 +15,7 @@ func newListFrontendsHandler() *listFrontendsHandler { func (h *listFrontendsHandler) Handle(params admin.ListFrontendsParams, principal *rest_model_zrok.Principal) middleware.Responder { if !principal.Admin { - logrus.Errorf("invalid admin principal") + logrus.Error("invalid admin principal") return admin.NewListFrontendsUnauthorized() } diff --git a/controller/listOrganizationMembers.go b/controller/listOrganizationMembers.go new file mode 100644 index 00000000..54390ddb --- /dev/null +++ b/controller/listOrganizationMembers.go @@ -0,0 +1,50 @@ +package controller + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/admin" + "github.com/sirupsen/logrus" +) + +type listOrganizationMembersHandler struct{} + +func newListOrganizationMembersHandler() *listOrganizationMembersHandler { + return &listOrganizationMembersHandler{} +} + +func (h *listOrganizationMembersHandler) Handle(params admin.ListOrganizationMembersParams, principal *rest_model_zrok.Principal) middleware.Responder { + if !principal.Admin { + logrus.Error("invalid admin principal") + return admin.NewListOrganizationMembersUnauthorized() + } + + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return admin.NewListOrganizationMembersInternalServerError() + } + defer func() { _ = trx.Rollback() }() + + org, err := str.FindOrganizationByToken(params.Body.Token, trx) + if err != nil { + logrus.Errorf("error finding organization by token: %v", err) + return admin.NewListOrganizationMembersInternalServerError() + } + if org == nil { + logrus.Errorf("organization '%v' not found", params.Body.Token) + return admin.NewListOrganizationMembersNotFound() + } + + emails, err := str.FindAccountsForOrganization(org.Id, trx) + if err != nil { + logrus.Errorf("error finding accounts for organization: %v", err) + return admin.NewListOrganizationMembersInternalServerError() + } + + var members []*admin.ListOrganizationMembersOKBodyMembersItems0 + for _, email := range emails { + members = append(members, &admin.ListOrganizationMembersOKBodyMembersItems0{Email: email}) + } + return admin.NewListOrganizationMembersOK().WithPayload(&admin.ListOrganizationMembersOKBody{Members: members}) +} diff --git a/controller/store/organizationMember.go b/controller/store/organizationMember.go index f61896ea..ec9f1108 100644 --- a/controller/store/organizationMember.go +++ b/controller/store/organizationMember.go @@ -17,6 +17,10 @@ func (str *Store) AddAccountToOrganization(acctId, orgId int, trx *sqlx.Tx) erro return nil } +func (str *Store) FindAccountsForOrganization(orgId int, trx *sqlx.Tx) ([]string, error) { + return nil, nil +} + func (str *Store) IsAccountInOrganization(acctId, orgId int, trx *sqlx.Tx) (bool, error) { stmt, err := trx.Prepare("select count(0) from organization_members where organization_id = $1 and account_id = $2") if err != nil { diff --git a/rest_client_zrok/admin/admin_client.go b/rest_client_zrok/admin/admin_client.go index 72944f41..0db767ca 100644 --- a/rest_client_zrok/admin/admin_client.go +++ b/rest_client_zrok/admin/admin_client.go @@ -460,7 +460,7 @@ func (a *Client) ListOrganizationMembers(params *ListOrganizationMembersParams, op := &runtime.ClientOperation{ ID: "listOrganizationMembers", Method: "POST", - PathPattern: "/organization/members", + PathPattern: "/organization/list", ProducesMediaTypes: []string{"application/zrok.v1+json"}, ConsumesMediaTypes: []string{"application/zrok.v1+json"}, Schemes: []string{"http"}, diff --git a/rest_client_zrok/admin/list_organization_members_responses.go b/rest_client_zrok/admin/list_organization_members_responses.go index 736b3d40..6a48998d 100644 --- a/rest_client_zrok/admin/list_organization_members_responses.go +++ b/rest_client_zrok/admin/list_organization_members_responses.go @@ -37,6 +37,12 @@ func (o *ListOrganizationMembersReader) ReadResponse(response runtime.ClientResp return nil, err } return nil, result + case 404: + result := NewListOrganizationMembersNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result case 500: result := NewListOrganizationMembersInternalServerError() if err := result.readResponse(response, consumer, o.formats); err != nil { @@ -44,7 +50,7 @@ func (o *ListOrganizationMembersReader) ReadResponse(response runtime.ClientResp } return nil, result default: - return nil, runtime.NewAPIError("[POST /organization/members] listOrganizationMembers", response, response.Code()) + return nil, runtime.NewAPIError("[POST /organization/list] listOrganizationMembers", response, response.Code()) } } @@ -93,11 +99,11 @@ func (o *ListOrganizationMembersOK) Code() int { } func (o *ListOrganizationMembersOK) Error() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersOK %+v", 200, o.Payload) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersOK %+v", 200, o.Payload) } func (o *ListOrganizationMembersOK) String() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersOK %+v", 200, o.Payload) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersOK %+v", 200, o.Payload) } func (o *ListOrganizationMembersOK) GetPayload() *ListOrganizationMembersOKBody { @@ -160,11 +166,11 @@ func (o *ListOrganizationMembersUnauthorized) Code() int { } func (o *ListOrganizationMembersUnauthorized) Error() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersUnauthorized ", 401) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersUnauthorized ", 401) } func (o *ListOrganizationMembersUnauthorized) String() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersUnauthorized ", 401) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersUnauthorized ", 401) } func (o *ListOrganizationMembersUnauthorized) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { @@ -172,6 +178,62 @@ func (o *ListOrganizationMembersUnauthorized) readResponse(response runtime.Clie return nil } +// NewListOrganizationMembersNotFound creates a ListOrganizationMembersNotFound with default headers values +func NewListOrganizationMembersNotFound() *ListOrganizationMembersNotFound { + return &ListOrganizationMembersNotFound{} +} + +/* +ListOrganizationMembersNotFound describes a response with status code 404, with default header values. + +not found +*/ +type ListOrganizationMembersNotFound struct { +} + +// IsSuccess returns true when this list organization members not found response has a 2xx status code +func (o *ListOrganizationMembersNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this list organization members not found response has a 3xx status code +func (o *ListOrganizationMembersNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this list organization members not found response has a 4xx status code +func (o *ListOrganizationMembersNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this list organization members not found response has a 5xx status code +func (o *ListOrganizationMembersNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this list organization members not found response a status code equal to that given +func (o *ListOrganizationMembersNotFound) IsCode(code int) bool { + return code == 404 +} + +// Code gets the status code for the list organization members not found response +func (o *ListOrganizationMembersNotFound) Code() int { + return 404 +} + +func (o *ListOrganizationMembersNotFound) Error() string { + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersNotFound ", 404) +} + +func (o *ListOrganizationMembersNotFound) String() string { + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersNotFound ", 404) +} + +func (o *ListOrganizationMembersNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + // NewListOrganizationMembersInternalServerError creates a ListOrganizationMembersInternalServerError with default headers values func NewListOrganizationMembersInternalServerError() *ListOrganizationMembersInternalServerError { return &ListOrganizationMembersInternalServerError{} @@ -216,11 +278,11 @@ func (o *ListOrganizationMembersInternalServerError) Code() int { } func (o *ListOrganizationMembersInternalServerError) Error() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersInternalServerError ", 500) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersInternalServerError ", 500) } func (o *ListOrganizationMembersInternalServerError) String() string { - return fmt.Sprintf("[POST /organization/members][%d] listOrganizationMembersInternalServerError ", 500) + return fmt.Sprintf("[POST /organization/list][%d] listOrganizationMembersInternalServerError ", 500) } func (o *ListOrganizationMembersInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 38fe5bd6..86268d6b 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -950,7 +950,7 @@ func init() { } } }, - "/organization/members": { + "/organization/list": { "post": { "security": [ { @@ -995,6 +995,9 @@ func init() { "401": { "description": "unauthorized" }, + "404": { + "description": "not found" + }, "500": { "description": "internal server error" } @@ -3007,7 +3010,7 @@ func init() { } } }, - "/organization/members": { + "/organization/list": { "post": { "security": [ { @@ -3048,6 +3051,9 @@ func init() { "401": { "description": "unauthorized" }, + "404": { + "description": "not found" + }, "500": { "description": "internal server error" } diff --git a/rest_server_zrok/operations/admin/list_organization_members.go b/rest_server_zrok/operations/admin/list_organization_members.go index d5d0c5bd..d12d3cb8 100644 --- a/rest_server_zrok/operations/admin/list_organization_members.go +++ b/rest_server_zrok/operations/admin/list_organization_members.go @@ -37,7 +37,7 @@ func NewListOrganizationMembers(ctx *middleware.Context, handler ListOrganizatio } /* - ListOrganizationMembers swagger:route POST /organization/members admin listOrganizationMembers + ListOrganizationMembers swagger:route POST /organization/list admin listOrganizationMembers ListOrganizationMembers list organization members API */ diff --git a/rest_server_zrok/operations/admin/list_organization_members_responses.go b/rest_server_zrok/operations/admin/list_organization_members_responses.go index 615e5417..eb58015b 100644 --- a/rest_server_zrok/operations/admin/list_organization_members_responses.go +++ b/rest_server_zrok/operations/admin/list_organization_members_responses.go @@ -81,6 +81,31 @@ func (o *ListOrganizationMembersUnauthorized) WriteResponse(rw http.ResponseWrit rw.WriteHeader(401) } +// ListOrganizationMembersNotFoundCode is the HTTP code returned for type ListOrganizationMembersNotFound +const ListOrganizationMembersNotFoundCode int = 404 + +/* +ListOrganizationMembersNotFound not found + +swagger:response listOrganizationMembersNotFound +*/ +type ListOrganizationMembersNotFound struct { +} + +// NewListOrganizationMembersNotFound creates ListOrganizationMembersNotFound with default headers values +func NewListOrganizationMembersNotFound() *ListOrganizationMembersNotFound { + + return &ListOrganizationMembersNotFound{} +} + +// WriteResponse to the client +func (o *ListOrganizationMembersNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(404) +} + // ListOrganizationMembersInternalServerErrorCode is the HTTP code returned for type ListOrganizationMembersInternalServerError const ListOrganizationMembersInternalServerErrorCode int = 500 diff --git a/rest_server_zrok/operations/admin/list_organization_members_urlbuilder.go b/rest_server_zrok/operations/admin/list_organization_members_urlbuilder.go index 62423a6a..c2a9006d 100644 --- a/rest_server_zrok/operations/admin/list_organization_members_urlbuilder.go +++ b/rest_server_zrok/operations/admin/list_organization_members_urlbuilder.go @@ -35,7 +35,7 @@ func (o *ListOrganizationMembersURL) SetBasePath(bp string) { func (o *ListOrganizationMembersURL) Build() (*url.URL, error) { var _result url.URL - var _path = "/organization/members" + var _path = "/organization/list" _basePath := o._basePath if _basePath == "" { diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index 0baf6e08..9ea40e10 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -678,7 +678,7 @@ func (o *ZrokAPI) initHandlerCache() { if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } - o.handlers["POST"]["/organization/members"] = admin.NewListOrganizationMembers(o.context, o.AdminListOrganizationMembersHandler) + o.handlers["POST"]["/organization/list"] = admin.NewListOrganizationMembers(o.context, o.AdminListOrganizationMembersHandler) if o.handlers["POST"] == nil { o.handlers["POST"] = make(map[string]http.Handler) } diff --git a/sdk/nodejs/sdk/src/zrok/api/api/adminApi.ts b/sdk/nodejs/sdk/src/zrok/api/api/adminApi.ts index b0f0f752..394a94dc 100644 --- a/sdk/nodejs/sdk/src/zrok/api/api/adminApi.ts +++ b/sdk/nodejs/sdk/src/zrok/api/api/adminApi.ts @@ -724,7 +724,7 @@ export class AdminApi { * @param body */ public async listOrganizationMembers (body?: RegenerateToken200Response, options: {headers: {[name: string]: string}} = {headers: {}}) : Promise<{ response: http.IncomingMessage; body: ListOrganizationMembers200Response; }> { - const localVarPath = this.basePath + '/organization/members'; + const localVarPath = this.basePath + '/organization/list'; let localVarQueryParameters: any = {}; let localVarHeaderParams: any = (Object).assign({}, this._defaultHeaders); const produces = ['application/zrok.v1+json']; diff --git a/sdk/python/sdk/zrok/zrok_api/__init__.py b/sdk/python/sdk/zrok/zrok_api/__init__.py index cab88172..1be1da7b 100644 --- a/sdk/python/sdk/zrok/zrok_api/__init__.py +++ b/sdk/python/sdk/zrok/zrok_api/__init__.py @@ -56,7 +56,7 @@ from zrok_api.models.metrics_sample import MetricsSample from zrok_api.models.organization_add_body import OrganizationAddBody from zrok_api.models.organization_body import OrganizationBody from zrok_api.models.organization_body1 import OrganizationBody1 -from zrok_api.models.organization_members_body import OrganizationMembersBody +from zrok_api.models.organization_list_body import OrganizationListBody from zrok_api.models.organization_remove_body import OrganizationRemoveBody from zrok_api.models.overview import Overview from zrok_api.models.password_requirements import PasswordRequirements diff --git a/sdk/python/sdk/zrok/zrok_api/api/admin_api.py b/sdk/python/sdk/zrok/zrok_api/api/admin_api.py index 9007e022..b5ba9eb2 100644 --- a/sdk/python/sdk/zrok/zrok_api/api/admin_api.py +++ b/sdk/python/sdk/zrok/zrok_api/api/admin_api.py @@ -943,7 +943,7 @@ class AdminApi(object): >>> result = thread.get() :param async_req bool - :param OrganizationMembersBody body: + :param OrganizationListBody body: :return: InlineResponse2001 If the method is called asynchronously, returns the request thread. @@ -964,7 +964,7 @@ class AdminApi(object): >>> result = thread.get() :param async_req bool - :param OrganizationMembersBody body: + :param OrganizationListBody body: :return: InlineResponse2001 If the method is called asynchronously, returns the request thread. @@ -1012,7 +1012,7 @@ class AdminApi(object): auth_settings = ['key'] # noqa: E501 return self.api_client.call_api( - '/organization/members', 'POST', + '/organization/list', 'POST', path_params, query_params, header_params, diff --git a/sdk/python/sdk/zrok/zrok_api/models/__init__.py b/sdk/python/sdk/zrok/zrok_api/models/__init__.py index b987cf91..1b78fad6 100644 --- a/sdk/python/sdk/zrok/zrok_api/models/__init__.py +++ b/sdk/python/sdk/zrok/zrok_api/models/__init__.py @@ -46,7 +46,7 @@ from zrok_api.models.metrics_sample import MetricsSample from zrok_api.models.organization_add_body import OrganizationAddBody from zrok_api.models.organization_body import OrganizationBody from zrok_api.models.organization_body1 import OrganizationBody1 -from zrok_api.models.organization_members_body import OrganizationMembersBody +from zrok_api.models.organization_list_body import OrganizationListBody from zrok_api.models.organization_remove_body import OrganizationRemoveBody from zrok_api.models.overview import Overview from zrok_api.models.password_requirements import PasswordRequirements diff --git a/sdk/python/sdk/zrok/zrok_api/models/organization_list_body.py b/sdk/python/sdk/zrok/zrok_api/models/organization_list_body.py new file mode 100644 index 00000000..4b783851 --- /dev/null +++ b/sdk/python/sdk/zrok/zrok_api/models/organization_list_body.py @@ -0,0 +1,110 @@ +# coding: utf-8 + +""" + zrok + + zrok client access # noqa: E501 + + OpenAPI spec version: 0.3.0 + + Generated by: https://github.com/swagger-api/swagger-codegen.git +""" + +import pprint +import re # noqa: F401 + +import six + +class OrganizationListBody(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Do not edit the class manually. + """ + """ + Attributes: + swagger_types (dict): The key is attribute name + and the value is attribute type. + attribute_map (dict): The key is attribute name + and the value is json key in definition. + """ + swagger_types = { + 'token': 'str' + } + + attribute_map = { + 'token': 'token' + } + + def __init__(self, token=None): # noqa: E501 + """OrganizationListBody - a model defined in Swagger""" # noqa: E501 + self._token = None + self.discriminator = None + if token is not None: + self.token = token + + @property + def token(self): + """Gets the token of this OrganizationListBody. # noqa: E501 + + + :return: The token of this OrganizationListBody. # noqa: E501 + :rtype: str + """ + return self._token + + @token.setter + def token(self, token): + """Sets the token of this OrganizationListBody. + + + :param token: The token of this OrganizationListBody. # noqa: E501 + :type: str + """ + + self._token = token + + def to_dict(self): + """Returns the model properties as a dict""" + result = {} + + for attr, _ in six.iteritems(self.swagger_types): + value = getattr(self, attr) + if isinstance(value, list): + result[attr] = list(map( + lambda x: x.to_dict() if hasattr(x, "to_dict") else x, + value + )) + elif hasattr(value, "to_dict"): + result[attr] = value.to_dict() + elif isinstance(value, dict): + result[attr] = dict(map( + lambda item: (item[0], item[1].to_dict()) + if hasattr(item[1], "to_dict") else item, + value.items() + )) + else: + result[attr] = value + if issubclass(OrganizationListBody, dict): + for key, value in self.items(): + result[key] = value + + return result + + def to_str(self): + """Returns the string representation of the model""" + return pprint.pformat(self.to_dict()) + + def __repr__(self): + """For `print` and `pprint`""" + return self.to_str() + + def __eq__(self, other): + """Returns true if both objects are equal""" + if not isinstance(other, OrganizationListBody): + return False + + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """Returns true if both objects are not equal""" + return not self == other diff --git a/specs/zrok.yml b/specs/zrok.yml index ff16f13a..fc20d9b7 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -452,7 +452,7 @@ paths: 500: description: internal server error - /organization/members: + /organization/list: post: tags: - admin @@ -479,6 +479,8 @@ paths: type: string 401: description: unauthorized + 404: + description: not found 500: description: internal server error diff --git a/ui/src/api/admin.js b/ui/src/api/admin.js index d1f882df..59ebbbac 100644 --- a/ui/src/api/admin.js +++ b/ui/src/api/admin.js @@ -309,7 +309,7 @@ const addOrganizationMemberOperation = { } const listOrganizationMembersOperation = { - path: '/organization/members', + path: '/organization/list', contentTypes: ['application/zrok.v1+json'], method: 'post', security: [