From 917a4d3f22adc10379e91e005f963a93818a56c7 Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Mon, 9 Dec 2024 16:42:30 -0500 Subject: [PATCH] list members of organization (#537) --- cmd/zrok/adminListOrgMembers.go | 61 ++++++++ controller/listOrganizationMembers.go | 8 +- controller/store/organizationMember.go | 21 ++- .../list_organization_members_responses.go | 3 + rest_server_zrok/embedded_spec.go | 6 + .../admin/list_organization_members.go | 3 + .../sdk/src/zrok/api/.openapi-generator/FILES | 1 + .../listOrganizationMembers200Response.ts | 6 +- ...anizationMembers200ResponseMembersInner.ts | 37 +++++ sdk/nodejs/sdk/src/zrok/api/model/models.ts | 3 + sdk/python/sdk/zrok/zrok_api/__init__.py | 1 + .../sdk/zrok/zrok_api/models/__init__.py | 1 + .../zrok_api/models/inline_response2001.py | 6 +- .../models/inline_response2001_members.py | 136 ++++++++++++++++++ specs/zrok.yml | 2 + 15 files changed, 283 insertions(+), 12 deletions(-) create mode 100644 cmd/zrok/adminListOrgMembers.go create mode 100644 sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200ResponseMembersInner.ts create mode 100644 sdk/python/sdk/zrok/zrok_api/models/inline_response2001_members.py diff --git a/cmd/zrok/adminListOrgMembers.go b/cmd/zrok/adminListOrgMembers.go new file mode 100644 index 00000000..0a626379 --- /dev/null +++ b/cmd/zrok/adminListOrgMembers.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + "github.com/jedib0t/go-pretty/v6/table" + "github.com/openziti/zrok/environment" + "github.com/openziti/zrok/rest_client_zrok/admin" + "github.com/spf13/cobra" + "os" +) + +func init() { + adminListCmd.AddCommand(newAdminListOrgMembersCommand().cmd) +} + +type adminListOrgMembersCommand struct { + cmd *cobra.Command +} + +func newAdminListOrgMembersCommand() *adminListOrgMembersCommand { + cmd := &cobra.Command{ + Use: "org-members ", + Aliases: []string{"members"}, + Short: "List the members of the specified organization", + Args: cobra.ExactArgs(1), + } + command := &adminListOrgMembersCommand{cmd: cmd} + cmd.Run = command.run + return command +} + +func (cmd *adminListOrgMembersCommand) run(_ *cobra.Command, args []string) { + env, err := environment.LoadRoot() + if err != nil { + panic(err) + } + + zrok, err := env.Client() + if err != nil { + panic(err) + } + + req := admin.NewListOrganizationMembersParams() + req.Body.Token = args[0] + + resp, err := zrok.Admin.ListOrganizationMembers(req, mustGetAdminAuth()) + if err != nil { + panic(err) + } + + fmt.Println() + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.SetStyle(table.StyleColoredDark) + t.AppendHeader(table.Row{"Account Email", "Admin?"}) + for _, member := range resp.Payload.Members { + t.AppendRow(table.Row{member.Email, member.Admin}) + } + t.Render() + fmt.Println() +} diff --git a/controller/listOrganizationMembers.go b/controller/listOrganizationMembers.go index 54390ddb..e101bc49 100644 --- a/controller/listOrganizationMembers.go +++ b/controller/listOrganizationMembers.go @@ -29,22 +29,22 @@ func (h *listOrganizationMembersHandler) Handle(params admin.ListOrganizationMem org, err := str.FindOrganizationByToken(params.Body.Token, trx) if err != nil { logrus.Errorf("error finding organization by token: %v", err) - return admin.NewListOrganizationMembersInternalServerError() + return admin.NewListOrganizationMembersNotFound() } if org == nil { logrus.Errorf("organization '%v' not found", params.Body.Token) return admin.NewListOrganizationMembersNotFound() } - emails, err := str.FindAccountsForOrganization(org.Id, trx) + oms, 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}) + for _, om := range oms { + members = append(members, &admin.ListOrganizationMembersOKBodyMembersItems0{Email: om.Email, Admin: om.Admin}) } return admin.NewListOrganizationMembersOK().WithPayload(&admin.ListOrganizationMembersOKBody{Members: members}) } diff --git a/controller/store/organizationMember.go b/controller/store/organizationMember.go index 7d3c5585..041a4736 100644 --- a/controller/store/organizationMember.go +++ b/controller/store/organizationMember.go @@ -17,8 +17,25 @@ func (str *Store) AddAccountToOrganization(acctId, orgId int, admin bool, trx *s return nil } -func (str *Store) FindAccountsForOrganization(orgId int, trx *sqlx.Tx) ([]string, error) { - return nil, nil +type OrganizationMember struct { + Email string + Admin bool +} + +func (str *Store) FindAccountsForOrganization(orgId int, trx *sqlx.Tx) ([]*OrganizationMember, error) { + rows, err := trx.Queryx("select organization_members.admin, accounts.email from organization_members, accounts where organization_members.organization_id = $1 and organization_members.account_id = accounts.id", orgId) + if err != nil { + return nil, errors.Wrap(err, "error querying organization members") + } + var members []*OrganizationMember + for rows.Next() { + om := &OrganizationMember{} + if err := rows.StructScan(&om); err != nil { + return nil, errors.Wrap(err, "error scanning account email") + } + members = append(members, om) + } + return members, nil } func (str *Store) IsAccountInOrganization(acctId, orgId int, trx *sqlx.Tx) (bool, error) { diff --git a/rest_client_zrok/admin/list_organization_members_responses.go b/rest_client_zrok/admin/list_organization_members_responses.go index 6a48998d..d22940e5 100644 --- a/rest_client_zrok/admin/list_organization_members_responses.go +++ b/rest_client_zrok/admin/list_organization_members_responses.go @@ -441,6 +441,9 @@ swagger:model ListOrganizationMembersOKBodyMembersItems0 */ type ListOrganizationMembersOKBodyMembersItems0 struct { + // admin + Admin bool `json:"admin,omitempty"` + // email Email string `json:"email,omitempty"` } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 02875bdf..810f00c1 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -992,6 +992,9 @@ func init() { "type": "array", "items": { "properties": { + "admin": { + "type": "boolean" + }, "email": { "type": "string" } @@ -3512,6 +3515,9 @@ func init() { "definitions": { "MembersItems0": { "properties": { + "admin": { + "type": "boolean" + }, "email": { "type": "string" } diff --git a/rest_server_zrok/operations/admin/list_organization_members.go b/rest_server_zrok/operations/admin/list_organization_members.go index d12d3cb8..1b6a05c1 100644 --- a/rest_server_zrok/operations/admin/list_organization_members.go +++ b/rest_server_zrok/operations/admin/list_organization_members.go @@ -223,6 +223,9 @@ func (o *ListOrganizationMembersOKBody) UnmarshalBinary(b []byte) error { // swagger:model ListOrganizationMembersOKBodyMembersItems0 type ListOrganizationMembersOKBodyMembersItems0 struct { + // admin + Admin bool `json:"admin,omitempty"` + // email Email string `json:"email,omitempty"` } diff --git a/sdk/nodejs/sdk/src/zrok/api/.openapi-generator/FILES b/sdk/nodejs/sdk/src/zrok/api/.openapi-generator/FILES index 702edf4c..2931720e 100644 --- a/sdk/nodejs/sdk/src/zrok/api/.openapi-generator/FILES +++ b/sdk/nodejs/sdk/src/zrok/api/.openapi-generator/FILES @@ -29,6 +29,7 @@ model/grantsRequest.ts model/inviteRequest.ts model/inviteTokenGenerateRequest.ts model/listOrganizationMembers200Response.ts +model/listOrganizationMembers200ResponseMembersInner.ts model/loginRequest.ts model/metrics.ts model/metricsSample.ts diff --git a/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200Response.ts b/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200Response.ts index 5e180b79..01452eca 100644 --- a/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200Response.ts +++ b/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200Response.ts @@ -11,10 +11,10 @@ */ import { RequestFile } from './models'; -import { GrantsRequest } from './grantsRequest'; +import { ListOrganizationMembers200ResponseMembersInner } from './listOrganizationMembers200ResponseMembersInner'; export class ListOrganizationMembers200Response { - 'members'?: Array; + 'members'?: Array; static discriminator: string | undefined = undefined; @@ -22,7 +22,7 @@ export class ListOrganizationMembers200Response { { "name": "members", "baseName": "members", - "type": "Array" + "type": "Array" } ]; static getAttributeTypeMap() { diff --git a/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200ResponseMembersInner.ts b/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200ResponseMembersInner.ts new file mode 100644 index 00000000..5c4faa33 --- /dev/null +++ b/sdk/nodejs/sdk/src/zrok/api/model/listOrganizationMembers200ResponseMembersInner.ts @@ -0,0 +1,37 @@ +/** + * zrok + * zrok client access + * + * The version of the OpenAPI document: 0.3.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { RequestFile } from './models'; + +export class ListOrganizationMembers200ResponseMembersInner { + 'email'?: string; + 'admin'?: boolean; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{name: string, baseName: string, type: string}> = [ + { + "name": "email", + "baseName": "email", + "type": "string" + }, + { + "name": "admin", + "baseName": "admin", + "type": "boolean" + } ]; + + static getAttributeTypeMap() { + return ListOrganizationMembers200ResponseMembersInner.attributeTypeMap; + } +} + diff --git a/sdk/nodejs/sdk/src/zrok/api/model/models.ts b/sdk/nodejs/sdk/src/zrok/api/model/models.ts index 77765e42..32450511 100644 --- a/sdk/nodejs/sdk/src/zrok/api/model/models.ts +++ b/sdk/nodejs/sdk/src/zrok/api/model/models.ts @@ -23,6 +23,7 @@ export * from './grantsRequest'; export * from './inviteRequest'; export * from './inviteTokenGenerateRequest'; export * from './listOrganizationMembers200Response'; +export * from './listOrganizationMembers200ResponseMembersInner'; export * from './loginRequest'; export * from './metrics'; export * from './metricsSample'; @@ -83,6 +84,7 @@ import { GrantsRequest } from './grantsRequest'; import { InviteRequest } from './inviteRequest'; import { InviteTokenGenerateRequest } from './inviteTokenGenerateRequest'; import { ListOrganizationMembers200Response } from './listOrganizationMembers200Response'; +import { ListOrganizationMembers200ResponseMembersInner } from './listOrganizationMembers200ResponseMembersInner'; import { LoginRequest } from './loginRequest'; import { Metrics } from './metrics'; import { MetricsSample } from './metricsSample'; @@ -151,6 +153,7 @@ let typeMap: {[index: string]: any} = { "InviteRequest": InviteRequest, "InviteTokenGenerateRequest": InviteTokenGenerateRequest, "ListOrganizationMembers200Response": ListOrganizationMembers200Response, + "ListOrganizationMembers200ResponseMembersInner": ListOrganizationMembers200ResponseMembersInner, "LoginRequest": LoginRequest, "Metrics": Metrics, "MetricsSample": MetricsSample, diff --git a/sdk/python/sdk/zrok/zrok_api/__init__.py b/sdk/python/sdk/zrok/zrok_api/__init__.py index 1be1da7b..0a5dd923 100644 --- a/sdk/python/sdk/zrok/zrok_api/__init__.py +++ b/sdk/python/sdk/zrok/zrok_api/__init__.py @@ -46,6 +46,7 @@ from zrok_api.models.grants_body import GrantsBody from zrok_api.models.identity_body import IdentityBody from zrok_api.models.inline_response200 import InlineResponse200 from zrok_api.models.inline_response2001 import InlineResponse2001 +from zrok_api.models.inline_response2001_members import InlineResponse2001Members from zrok_api.models.inline_response201 import InlineResponse201 from zrok_api.models.invite_request import InviteRequest from zrok_api.models.invite_token_generate_request import InviteTokenGenerateRequest diff --git a/sdk/python/sdk/zrok/zrok_api/models/__init__.py b/sdk/python/sdk/zrok/zrok_api/models/__init__.py index 1b78fad6..e65e9399 100644 --- a/sdk/python/sdk/zrok/zrok_api/models/__init__.py +++ b/sdk/python/sdk/zrok/zrok_api/models/__init__.py @@ -36,6 +36,7 @@ from zrok_api.models.grants_body import GrantsBody from zrok_api.models.identity_body import IdentityBody from zrok_api.models.inline_response200 import InlineResponse200 from zrok_api.models.inline_response2001 import InlineResponse2001 +from zrok_api.models.inline_response2001_members import InlineResponse2001Members from zrok_api.models.inline_response201 import InlineResponse201 from zrok_api.models.invite_request import InviteRequest from zrok_api.models.invite_token_generate_request import InviteTokenGenerateRequest diff --git a/sdk/python/sdk/zrok/zrok_api/models/inline_response2001.py b/sdk/python/sdk/zrok/zrok_api/models/inline_response2001.py index 1b32b09e..214eca09 100644 --- a/sdk/python/sdk/zrok/zrok_api/models/inline_response2001.py +++ b/sdk/python/sdk/zrok/zrok_api/models/inline_response2001.py @@ -28,7 +28,7 @@ class InlineResponse2001(object): and the value is json key in definition. """ swagger_types = { - 'members': 'list[GrantsBody]' + 'members': 'list[InlineResponse2001Members]' } attribute_map = { @@ -48,7 +48,7 @@ class InlineResponse2001(object): :return: The members of this InlineResponse2001. # noqa: E501 - :rtype: list[GrantsBody] + :rtype: list[InlineResponse2001Members] """ return self._members @@ -58,7 +58,7 @@ class InlineResponse2001(object): :param members: The members of this InlineResponse2001. # noqa: E501 - :type: list[GrantsBody] + :type: list[InlineResponse2001Members] """ self._members = members diff --git a/sdk/python/sdk/zrok/zrok_api/models/inline_response2001_members.py b/sdk/python/sdk/zrok/zrok_api/models/inline_response2001_members.py new file mode 100644 index 00000000..a5fbc3e6 --- /dev/null +++ b/sdk/python/sdk/zrok/zrok_api/models/inline_response2001_members.py @@ -0,0 +1,136 @@ +# 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 InlineResponse2001Members(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 = { + 'email': 'str', + 'admin': 'bool' + } + + attribute_map = { + 'email': 'email', + 'admin': 'admin' + } + + def __init__(self, email=None, admin=None): # noqa: E501 + """InlineResponse2001Members - a model defined in Swagger""" # noqa: E501 + self._email = None + self._admin = None + self.discriminator = None + if email is not None: + self.email = email + if admin is not None: + self.admin = admin + + @property + def email(self): + """Gets the email of this InlineResponse2001Members. # noqa: E501 + + + :return: The email of this InlineResponse2001Members. # noqa: E501 + :rtype: str + """ + return self._email + + @email.setter + def email(self, email): + """Sets the email of this InlineResponse2001Members. + + + :param email: The email of this InlineResponse2001Members. # noqa: E501 + :type: str + """ + + self._email = email + + @property + def admin(self): + """Gets the admin of this InlineResponse2001Members. # noqa: E501 + + + :return: The admin of this InlineResponse2001Members. # noqa: E501 + :rtype: bool + """ + return self._admin + + @admin.setter + def admin(self, admin): + """Sets the admin of this InlineResponse2001Members. + + + :param admin: The admin of this InlineResponse2001Members. # noqa: E501 + :type: bool + """ + + self._admin = admin + + 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(InlineResponse2001Members, 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, InlineResponse2001Members): + 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 2c3eace0..3c40cfe5 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -483,6 +483,8 @@ paths: properties: email: type: string + admin: + type: boolean 401: description: unauthorized 404: