diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6de8025..98ea94e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,7 @@ jobs: echo "These files need gofmt:"; echo "$unformatted"; exit 1 fi - # make test starts the quay.io/authorizer/authorizer:2.4.0-rc.9 container + # make test starts the quay.io/authorizer/authorizer:2.4.0-rc.13 container # (docker is preinstalled on ubuntu-latest), runs the integration suite # across graphql/rest/grpc, then tears the container down. - name: Integration tests diff --git a/Makefile b/Makefile index 7db7c3d..b4e4c9e 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # 2. Run tests: make test # Docker image for authorizer server -AUTHORIZER_IMAGE := quay.io/authorizer/authorizer:2.4.0-rc.9 +AUTHORIZER_IMAGE := quay.io/authorizer/authorizer:2.4.0-rc.13 AUTHORIZER_CONTAINER := authorizer-test .PHONY: docker-up docker-down test diff --git a/admin_accessors.go b/admin_accessors.go new file mode 100644 index 0000000..ccb4fa3 --- /dev/null +++ b/admin_accessors.go @@ -0,0 +1,409 @@ +package authorizer + +import ( + authorizerv1 "github.com/authorizerdev/authorizer-proto-go/authorizer/v1" +) + +// Nil-safe accessors for the organization / org SSO / SCIM / org domain request +// types. The gRPC path builds a proto message field by field, and these keep +// that construction safe when the caller passes a nil request (valid for the +// list operations, whose GraphQL params are optional). +func (r *CreateOrganizationRequest) GetName() string { + if r == nil { + return "" + } + return r.Name +} +func (r *CreateOrganizationRequest) GetDisplayName() *string { + if r == nil { + return nil + } + return r.DisplayName +} +func (r *UpdateOrganizationRequest) GetID() string { + if r == nil { + return "" + } + return r.ID +} +func (r *UpdateOrganizationRequest) GetName() *string { + if r == nil { + return nil + } + return r.Name +} +func (r *UpdateOrganizationRequest) GetDisplayName() *string { + if r == nil { + return nil + } + return r.DisplayName +} +func (r *UpdateOrganizationRequest) GetEnabled() *bool { + if r == nil { + return nil + } + return r.Enabled +} +func (r *OrganizationRequest) GetID() string { + if r == nil { + return "" + } + return r.ID +} +func (r *AddOrgMemberRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *AddOrgMemberRequest) GetUserID() string { + if r == nil { + return "" + } + return r.UserID +} +func (r *AddOrgMemberRequest) GetRoles() []string { + if r == nil { + return nil + } + return r.Roles +} +func (r *RemoveOrgMemberRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *RemoveOrgMemberRequest) GetUserID() string { + if r == nil { + return "" + } + return r.UserID +} +func (r *ListOrgMembersRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *CreateOrgOIDCConnectionRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *CreateOrgOIDCConnectionRequest) GetName() string { + if r == nil { + return "" + } + return r.Name +} +func (r *CreateOrgOIDCConnectionRequest) GetIssuerURL() string { + if r == nil { + return "" + } + return r.IssuerURL +} +func (r *CreateOrgOIDCConnectionRequest) GetClientID() string { + if r == nil { + return "" + } + return r.ClientID +} +func (r *CreateOrgOIDCConnectionRequest) GetClientSecret() string { + if r == nil { + return "" + } + return r.ClientSecret +} +func (r *CreateOrgOIDCConnectionRequest) GetScopes() *string { + if r == nil { + return nil + } + return r.Scopes +} +func (r *CreateOrgOIDCConnectionRequest) GetRedirectURI() *string { + if r == nil { + return nil + } + return r.RedirectURI +} +func (r *UpdateOrgOIDCConnectionRequest) GetID() string { + if r == nil { + return "" + } + return r.ID +} +func (r *UpdateOrgOIDCConnectionRequest) GetName() *string { + if r == nil { + return nil + } + return r.Name +} +func (r *UpdateOrgOIDCConnectionRequest) GetIssuerURL() *string { + if r == nil { + return nil + } + return r.IssuerURL +} +func (r *UpdateOrgOIDCConnectionRequest) GetClientID() *string { + if r == nil { + return nil + } + return r.ClientID +} +func (r *UpdateOrgOIDCConnectionRequest) GetClientSecret() *string { + if r == nil { + return nil + } + return r.ClientSecret +} +func (r *UpdateOrgOIDCConnectionRequest) GetScopes() *string { + if r == nil { + return nil + } + return r.Scopes +} +func (r *UpdateOrgOIDCConnectionRequest) GetRedirectURI() *string { + if r == nil { + return nil + } + return r.RedirectURI +} +func (r *UpdateOrgOIDCConnectionRequest) GetIsActive() *bool { + if r == nil { + return nil + } + return r.IsActive +} +func (r *OrgOIDCConnectionRequest) GetID() *string { + if r == nil { + return nil + } + return r.ID +} +func (r *OrgOIDCConnectionRequest) GetOrgID() *string { + if r == nil { + return nil + } + return r.OrgID +} +func (r *CreateOrgSAMLConnectionRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *CreateOrgSAMLConnectionRequest) GetName() string { + if r == nil { + return "" + } + return r.Name +} +func (r *CreateOrgSAMLConnectionRequest) GetIdpEntityID() string { + if r == nil { + return "" + } + return r.IdpEntityID +} +func (r *CreateOrgSAMLConnectionRequest) GetIdpSSOURL() string { + if r == nil { + return "" + } + return r.IdpSSOURL +} +func (r *CreateOrgSAMLConnectionRequest) GetIdpCertificate() string { + if r == nil { + return "" + } + return r.IdpCertificate +} +func (r *CreateOrgSAMLConnectionRequest) GetSpEntityID() *string { + if r == nil { + return nil + } + return r.SpEntityID +} +func (r *CreateOrgSAMLConnectionRequest) GetAcsURL() *string { + if r == nil { + return nil + } + return r.AcsURL +} +func (r *CreateOrgSAMLConnectionRequest) GetAttributeMapping() *string { + if r == nil { + return nil + } + return r.AttributeMapping +} +func (r *CreateOrgSAMLConnectionRequest) GetAllowIdpInitiated() *bool { + if r == nil { + return nil + } + return r.AllowIdpInitiated +} +func (r *UpdateOrgSAMLConnectionRequest) GetID() string { + if r == nil { + return "" + } + return r.ID +} +func (r *UpdateOrgSAMLConnectionRequest) GetName() *string { + if r == nil { + return nil + } + return r.Name +} +func (r *UpdateOrgSAMLConnectionRequest) GetIdpEntityID() *string { + if r == nil { + return nil + } + return r.IdpEntityID +} +func (r *UpdateOrgSAMLConnectionRequest) GetIdpSSOURL() *string { + if r == nil { + return nil + } + return r.IdpSSOURL +} +func (r *UpdateOrgSAMLConnectionRequest) GetIdpCertificate() *string { + if r == nil { + return nil + } + return r.IdpCertificate +} +func (r *UpdateOrgSAMLConnectionRequest) GetSpEntityID() *string { + if r == nil { + return nil + } + return r.SpEntityID +} +func (r *UpdateOrgSAMLConnectionRequest) GetAcsURL() *string { + if r == nil { + return nil + } + return r.AcsURL +} +func (r *UpdateOrgSAMLConnectionRequest) GetAttributeMapping() *string { + if r == nil { + return nil + } + return r.AttributeMapping +} +func (r *UpdateOrgSAMLConnectionRequest) GetAllowIdpInitiated() *bool { + if r == nil { + return nil + } + return r.AllowIdpInitiated +} +func (r *UpdateOrgSAMLConnectionRequest) GetIsActive() *bool { + if r == nil { + return nil + } + return r.IsActive +} +func (r *OrgSAMLConnectionRequest) GetID() *string { + if r == nil { + return nil + } + return r.ID +} +func (r *OrgSAMLConnectionRequest) GetOrgID() *string { + if r == nil { + return nil + } + return r.OrgID +} +func (r *CreateScimEndpointRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *ScimEndpointRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *UserOrganizationsRequest) GetUserID() string { + if r == nil { + return "" + } + return r.UserID +} +func (r *RequestOrgDomainRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *RequestOrgDomainRequest) GetDomain() string { + if r == nil { + return "" + } + return r.Domain +} +func (r *VerifyOrgDomainRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *VerifyOrgDomainRequest) GetDomain() string { + if r == nil { + return "" + } + return r.Domain +} +func (r *AddVerifiedOrgDomainRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} +func (r *AddVerifiedOrgDomainRequest) GetDomain() string { + if r == nil { + return "" + } + return r.Domain +} +func (r *DeleteOrgDomainRequest) GetDomain() string { + if r == nil { + return "" + } + return r.Domain +} +func (r *ListOrgDomainsRequest) GetOrgID() string { + if r == nil { + return "" + } + return r.OrgID +} + +// protoPagination converts the SDK pagination input to its proto equivalent, +// tolerating both a nil request and a nil pagination field. +func (r *ListOrganizationsRequest) protoPagination() *authorizerv1.PaginationRequest { + if r == nil || r.Pagination == nil { + return nil + } + return &authorizerv1.PaginationRequest{Page: r.Pagination.Page, Limit: r.Pagination.Limit} +} +func (r *ListOrgMembersRequest) protoPagination() *authorizerv1.PaginationRequest { + if r == nil || r.Pagination == nil { + return nil + } + return &authorizerv1.PaginationRequest{Page: r.Pagination.Page, Limit: r.Pagination.Limit} +} +func (r *UserOrganizationsRequest) protoPagination() *authorizerv1.PaginationRequest { + if r == nil || r.Pagination == nil { + return nil + } + return &authorizerv1.PaginationRequest{Page: r.Pagination.Page, Limit: r.Pagination.Limit} +} +func (r *ListOrgDomainsRequest) protoPagination() *authorizerv1.PaginationRequest { + if r == nil || r.Pagination == nil { + return nil + } + return &authorizerv1.PaginationRequest{Page: r.Pagination.Page, Limit: r.Pagination.Limit} +} diff --git a/admin_client.go b/admin_client.go index 3f854b3..9faf30d 100644 --- a/admin_client.go +++ b/admin_client.go @@ -6,6 +6,8 @@ import ( "fmt" "strings" + "google.golang.org/protobuf/proto" + authorizerv1 "github.com/authorizerdev/authorizer-proto-go/authorizer/v1" ) @@ -92,6 +94,22 @@ type adminMethodSpec struct { // it (e.g. _update_client returns Client, proto UpdateClientResponse{client}). graphqlWrap string + // responseUnwrap is the dual of graphqlWrap, for methods whose out is the + // bare domain object rather than the proto response message: it names the + // single field to unwrap from the REST/gRPC response before unmarshalling. + // Used by the organization / org SSO / SCIM / org domain methods, whose + // hand-written signatures predate the proto (server 2.4.0 added the RPCs) + // and are kept so adding the transports is not a breaking change. + // Leave empty when the response is read whole (a message, or a paginated + // list the domain type already mirrors). + responseUnwrap string + + // restResponse builds the proto response message the REST body is decoded + // into, for methods whose out is a hand-written domain type rather than the + // proto message itself. grpc-gateway emits int64 as a JSON string, which + // doREST only handles for proto.Message targets (via protojson). + restResponse func() proto.Message + // restMethod / restPath; empty restPath means rest-unsupported. restMethod string restPath string @@ -131,6 +149,13 @@ func (c *AuthorizerAdminClient) execute(spec adminMethodSpec, out interface{}) e if spec.restPath == "" { return unsupportedProtocol(spec.name, c.Protocol, spec.supported()) } + if spec.restResponse != nil { + msg := spec.restResponse() + if err := doREST(c.AuthorizerURL, spec.restMethod, spec.restPath, spec.restBody, c.ExtraHeaders, map[string]string{adminSecretHeader: c.AdminSecret}, msg); err != nil { + return err + } + return unwrapProto(msg, spec.responseUnwrap, out) + } return doREST(c.AuthorizerURL, spec.restMethod, spec.restPath, spec.restBody, c.ExtraHeaders, map[string]string{adminSecretHeader: c.AdminSecret}, out) case ProtocolGRPC: @@ -149,7 +174,7 @@ func (c *AuthorizerAdminClient) execute(spec adminMethodSpec, out interface{}) e if err != nil { return err } - return remarshal(resp, out) + return unwrapProto(resp, spec.responseUnwrap, out) default: // ProtocolGraphQL if spec.graphql == nil { @@ -190,3 +215,26 @@ func (c *AuthorizerAdminClient) executeGraphQL(req *GraphQLRequest) ([]byte, err } return uc.ExecuteGraphQL(req, map[string]string{adminSecretHeader: c.AdminSecret}) } + +// unwrapProto converts a proto response message into out, optionally pulling a +// single named field out of it first. Marshalling the proto struct with +// encoding/json (not protojson) is deliberate: it emits int64 as a JSON number, +// which the hand-written domain types can decode. A missing field leaves out at +// its zero value, matching how the graphql path treats an absent field. +func unwrapProto(msg interface{}, field string, out interface{}) error { + if out == nil { + return nil + } + if field == "" { + return remarshal(msg, out) + } + var envelope map[string]json.RawMessage + if err := remarshal(msg, &envelope); err != nil { + return err + } + raw, ok := envelope[field] + if !ok { + return nil + } + return json.Unmarshal(raw, out) +} diff --git a/admin_methods.go b/admin_methods.go index b679824..33d0e81 100644 --- a/admin_methods.go +++ b/admin_methods.go @@ -4,6 +4,8 @@ import ( "context" "net/http" + "google.golang.org/protobuf/proto" + authorizerv1 "github.com/authorizerdev/authorizer-proto-go/authorizer/v1" ) @@ -66,10 +68,14 @@ func (c *AuthorizerAdminClient) AdminLogin(req *authorizerv1.AdminLoginRequest) func (c *AuthorizerAdminClient) AdminLogout() (*authorizerv1.AdminLogoutResponse, error) { var res authorizerv1.AdminLogoutResponse err := c.execute(adminMethodSpec{ - name: "AdminLogout", - restMethod: http.MethodPost, - restPath: "/v1/admin/logout", - restBody: &authorizerv1.AdminLogoutRequest{}, + name: "AdminLogout", + graphql: &GraphQLRequest{ + Query: `mutation adminLogout { _admin_logout { message } }`, + }, + graphqlField: "_admin_logout", + restMethod: http.MethodPost, + restPath: "/v1/admin/logout", + restBody: &authorizerv1.AdminLogoutRequest{}, grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { return cli.AdminLogout(ctx, &authorizerv1.AdminLogoutRequest{}) }, @@ -88,9 +94,13 @@ func (c *AuthorizerAdminClient) AdminLogout() (*authorizerv1.AdminLogoutResponse func (c *AuthorizerAdminClient) AdminSession() (*authorizerv1.AdminSessionResponse, error) { var res authorizerv1.AdminSessionResponse err := c.execute(adminMethodSpec{ - name: "AdminSession", - restMethod: http.MethodGet, - restPath: "/v1/admin/session", + name: "AdminSession", + graphql: &GraphQLRequest{ + Query: `query adminSession { _admin_session { message } }`, + }, + graphqlField: "_admin_session", + restMethod: http.MethodGet, + restPath: "/v1/admin/session", grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { return cli.AdminSession(ctx, &authorizerv1.AdminSessionRequest{}) }, @@ -110,9 +120,14 @@ func (c *AuthorizerAdminClient) AdminSession() (*authorizerv1.AdminSessionRespon func (c *AuthorizerAdminClient) AdminMeta() (*authorizerv1.AdminMetaResponse, error) { var res authorizerv1.AdminMetaResponse err := c.execute(adminMethodSpec{ - name: "AdminMeta", - restMethod: http.MethodGet, - restPath: "/v1/admin/meta", + name: "AdminMeta", + graphql: &GraphQLRequest{ + Query: `query adminMeta { _admin_meta { roles default_roles protected_roles is_multi_factor_auth_service_enabled } }`, + }, + graphqlField: "_admin_meta", + graphqlWrap: "admin_meta", + restMethod: http.MethodGet, + restPath: "/v1/admin/meta", grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { return cli.AdminMeta(ctx, &authorizerv1.AdminMetaRequest{}) }, @@ -673,9 +688,14 @@ func (c *AuthorizerAdminClient) AuditLogs(req *authorizerv1.AuditLogsRequest) (* func (c *AuthorizerAdminClient) FgaGetModel() (*authorizerv1.FgaGetModelResponse, error) { var res authorizerv1.FgaGetModelResponse err := c.execute(adminMethodSpec{ - name: "FgaGetModel", - restMethod: http.MethodGet, - restPath: "/v1/admin/fga/model", + name: "FgaGetModel", + graphql: &GraphQLRequest{ + Query: `query adminFgaGetModel { _fga_get_model { id dsl } }`, + }, + graphqlField: "_fga_get_model", + graphqlWrap: "model", + restMethod: http.MethodGet, + restPath: "/v1/admin/fga/model", grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { return cli.FgaGetModel(ctx, &authorizerv1.FgaGetModelRequest{}) }, @@ -859,10 +879,14 @@ func (c *AuthorizerAdminClient) FgaExpand(req *authorizerv1.FgaExpandRequest) (* func (c *AuthorizerAdminClient) FgaReset() (*authorizerv1.FgaResetResponse, error) { var res authorizerv1.FgaResetResponse err := c.execute(adminMethodSpec{ - name: "FgaReset", - restMethod: http.MethodPost, - restPath: "/v1/admin/fga/reset", - restBody: &authorizerv1.FgaResetRequest{}, + name: "FgaReset", + graphql: &GraphQLRequest{ + Query: `mutation adminFgaReset { _fga_reset { message } }`, + }, + graphqlField: "_fga_reset", + restMethod: http.MethodPost, + restPath: "/v1/admin/fga/reset", + restBody: &authorizerv1.FgaResetRequest{}, grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { return cli.FgaReset(ctx, &authorizerv1.FgaResetRequest{}) }, @@ -1542,18 +1566,6 @@ func (c *AuthorizerAdminClient) GenerateJWTKeys(req *GenerateJWTKeysRequest) (*G // clear unsupported-protocol error. Types mirror the GraphQL schema. // --------------------------------------------------------------------------- -// gqlOnly runs a graphql-only admin operation (no REST path, no gRPC stub). -func (c *AuthorizerAdminClient) gqlOnly(name, query, field string, req, out interface{}) error { - return c.execute(adminMethodSpec{ - name: name, - graphql: &GraphQLRequest{ - Query: query, - Variables: map[string]interface{}{"data": req}, - }, - graphqlField: field, - }, out) -} - // PaginationRequest mirrors the GraphQL PaginationRequest input. type PaginationRequest struct { Limit int64 `json:"limit,omitempty"` @@ -1651,9 +1663,25 @@ type ListOrgMembersRequest struct { // CreateOrganization creates an organization (gql only). func (c *AuthorizerAdminClient) CreateOrganization(req *CreateOrganizationRequest) (*Organization, error) { var res Organization - if err := c.gqlOnly("CreateOrganization", - "mutation createOrganization($data: CreateOrganizationRequest!) { _create_organization(params: $data) { "+adminOrgFields+" } }", - "_create_organization", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "CreateOrganization", + graphql: &GraphQLRequest{ + Query: "mutation createOrganization($data: CreateOrganizationRequest!) { _create_organization(params: $data) { " + adminOrgFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_create_organization", + responseUnwrap: "organization", + restResponse: func() proto.Message { return &authorizerv1.CreateOrganizationResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/create_organization", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.CreateOrganization(ctx, &authorizerv1.CreateOrganizationRequest{ + Name: req.GetName(), DisplayName: req.GetDisplayName(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1662,9 +1690,25 @@ func (c *AuthorizerAdminClient) CreateOrganization(req *CreateOrganizationReques // UpdateOrganization updates an organization (gql only). func (c *AuthorizerAdminClient) UpdateOrganization(req *UpdateOrganizationRequest) (*Organization, error) { var res Organization - if err := c.gqlOnly("UpdateOrganization", - "mutation updateOrganization($data: UpdateOrganizationRequest!) { _update_organization(params: $data) { "+adminOrgFields+" } }", - "_update_organization", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "UpdateOrganization", + graphql: &GraphQLRequest{ + Query: "mutation updateOrganization($data: UpdateOrganizationRequest!) { _update_organization(params: $data) { " + adminOrgFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_update_organization", + responseUnwrap: "organization", + restResponse: func() proto.Message { return &authorizerv1.UpdateOrganizationResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/update_organization", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.UpdateOrganization(ctx, &authorizerv1.UpdateOrganizationRequest{ + Id: req.GetID(), Name: req.GetName(), DisplayName: req.GetDisplayName(), Enabled: req.GetEnabled(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1674,9 +1718,22 @@ func (c *AuthorizerAdminClient) UpdateOrganization(req *UpdateOrganizationReques // permanently removes the organization and its memberships/connections. func (c *AuthorizerAdminClient) DeleteOrganization(req *OrganizationRequest) (*Response, error) { var res Response - if err := c.gqlOnly("DeleteOrganization", - `mutation deleteOrganization($data: OrganizationRequest!) { _delete_organization(params: $data) { message } }`, - "_delete_organization", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "DeleteOrganization", + graphql: &GraphQLRequest{ + Query: `mutation deleteOrganization($data: OrganizationRequest!) { _delete_organization(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_delete_organization", + restResponse: func() proto.Message { return &authorizerv1.DeleteOrganizationResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/delete_organization", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.DeleteOrganization(ctx, &authorizerv1.DeleteOrganizationRequest{Id: req.GetID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1685,9 +1742,25 @@ func (c *AuthorizerAdminClient) DeleteOrganization(req *OrganizationRequest) (*R // AddOrgMember adds a user to an organization (gql only). func (c *AuthorizerAdminClient) AddOrgMember(req *AddOrgMemberRequest) (*OrgMember, error) { var res OrgMember - if err := c.gqlOnly("AddOrgMember", - "mutation addOrgMember($data: AddOrgMemberRequest!) { _add_org_member(params: $data) { "+adminOrgMemberFields+" } }", - "_add_org_member", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "AddOrgMember", + graphql: &GraphQLRequest{ + Query: "mutation addOrgMember($data: AddOrgMemberRequest!) { _add_org_member(params: $data) { " + adminOrgMemberFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_add_org_member", + responseUnwrap: "org_member", + restResponse: func() proto.Message { return &authorizerv1.AddOrgMemberResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/add_org_member", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.AddOrgMember(ctx, &authorizerv1.AddOrgMemberRequest{ + OrgId: req.GetOrgID(), UserId: req.GetUserID(), Roles: req.GetRoles(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1696,9 +1769,22 @@ func (c *AuthorizerAdminClient) AddOrgMember(req *AddOrgMemberRequest) (*OrgMemb // RemoveOrgMember removes a user from an organization (gql only). func (c *AuthorizerAdminClient) RemoveOrgMember(req *RemoveOrgMemberRequest) (*Response, error) { var res Response - if err := c.gqlOnly("RemoveOrgMember", - `mutation removeOrgMember($data: RemoveOrgMemberRequest!) { _remove_org_member(params: $data) { message } }`, - "_remove_org_member", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "RemoveOrgMember", + graphql: &GraphQLRequest{ + Query: `mutation removeOrgMember($data: RemoveOrgMemberRequest!) { _remove_org_member(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_remove_org_member", + restResponse: func() proto.Message { return &authorizerv1.RemoveOrgMemberResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/remove_org_member", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.RemoveOrgMember(ctx, &authorizerv1.RemoveOrgMemberRequest{OrgId: req.GetOrgID(), UserId: req.GetUserID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1707,9 +1793,23 @@ func (c *AuthorizerAdminClient) RemoveOrgMember(req *RemoveOrgMemberRequest) (*R // GetOrganization returns a single organization by id (gql only). func (c *AuthorizerAdminClient) GetOrganization(req *OrganizationRequest) (*Organization, error) { var res Organization - if err := c.gqlOnly("GetOrganization", - "query organization($data: OrganizationRequest!) { _organization(params: $data) { "+adminOrgFields+" } }", - "_organization", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "GetOrganization", + graphql: &GraphQLRequest{ + Query: "query organization($data: OrganizationRequest!) { _organization(params: $data) { " + adminOrgFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_organization", + responseUnwrap: "organization", + restResponse: func() proto.Message { return &authorizerv1.GetOrganizationResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/organization", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.GetOrganization(ctx, &authorizerv1.GetOrganizationRequest{Id: req.GetID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1718,9 +1818,22 @@ func (c *AuthorizerAdminClient) GetOrganization(req *OrganizationRequest) (*Orga // Organizations returns a paginated list of organizations (gql only). func (c *AuthorizerAdminClient) Organizations(req *ListOrganizationsRequest) (*Organizations, error) { var res Organizations - if err := c.gqlOnly("Organizations", - "query organizations($data: ListOrganizationsRequest) { _organizations(params: $data) { "+adminPaginationFields+" organizations { "+adminOrgFields+" } } }", - "_organizations", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "Organizations", + graphql: &GraphQLRequest{ + Query: "query organizations($data: ListOrganizationsRequest) { _organizations(params: $data) { " + adminPaginationFields + " organizations { " + adminOrgFields + " } } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_organizations", + restResponse: func() proto.Message { return &authorizerv1.OrganizationsResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/organizations", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.Organizations(ctx, &authorizerv1.OrganizationsRequest{Pagination: req.protoPagination()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1729,9 +1842,22 @@ func (c *AuthorizerAdminClient) Organizations(req *ListOrganizationsRequest) (*O // OrgMembers returns a paginated list of an organization's members (gql only). func (c *AuthorizerAdminClient) OrgMembers(req *ListOrgMembersRequest) (*OrgMembers, error) { var res OrgMembers - if err := c.gqlOnly("OrgMembers", - "query orgMembers($data: ListOrgMembersRequest!) { _org_members(params: $data) { "+adminPaginationFields+" org_members { "+adminOrgMemberFields+" } } }", - "_org_members", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "OrgMembers", + graphql: &GraphQLRequest{ + Query: "query orgMembers($data: ListOrgMembersRequest!) { _org_members(params: $data) { " + adminPaginationFields + " org_members { " + adminOrgMemberFields + " } } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_org_members", + restResponse: func() proto.Message { return &authorizerv1.OrgMembersResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/org_members", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.OrgMembers(ctx, &authorizerv1.OrgMembersRequest{OrgId: req.GetOrgID(), Pagination: req.protoPagination()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1787,9 +1913,27 @@ type OrgOIDCConnectionRequest struct { // CreateOrgOIDCConnection creates an org OIDC SSO connection (gql only). func (c *AuthorizerAdminClient) CreateOrgOIDCConnection(req *CreateOrgOIDCConnectionRequest) (*OrgOIDCConnection, error) { var res OrgOIDCConnection - if err := c.gqlOnly("CreateOrgOIDCConnection", - "mutation createOrgOidcConnection($data: CreateOrgOIDCConnectionRequest!) { _create_org_oidc_connection(params: $data) { "+adminOrgOIDCConnFields+" } }", - "_create_org_oidc_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "CreateOrgOIDCConnection", + graphql: &GraphQLRequest{ + Query: "mutation createOrgOidcConnection($data: CreateOrgOIDCConnectionRequest!) { _create_org_oidc_connection(params: $data) { " + adminOrgOIDCConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_create_org_oidc_connection", + responseUnwrap: "org_oidc_connection", + restResponse: func() proto.Message { return &authorizerv1.CreateOrgOidcConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/create_org_oidc_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.CreateOrgOidcConnection(ctx, &authorizerv1.CreateOrgOidcConnectionRequest{ + OrgId: req.GetOrgID(), Name: req.GetName(), IssuerUrl: req.GetIssuerURL(), + ClientId: req.GetClientID(), ClientSecret: req.GetClientSecret(), + Scopes: req.GetScopes(), RedirectUri: req.GetRedirectURI(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1798,9 +1942,27 @@ func (c *AuthorizerAdminClient) CreateOrgOIDCConnection(req *CreateOrgOIDCConnec // UpdateOrgOIDCConnection updates an org OIDC SSO connection (gql only). func (c *AuthorizerAdminClient) UpdateOrgOIDCConnection(req *UpdateOrgOIDCConnectionRequest) (*OrgOIDCConnection, error) { var res OrgOIDCConnection - if err := c.gqlOnly("UpdateOrgOIDCConnection", - "mutation updateOrgOidcConnection($data: UpdateOrgOIDCConnectionRequest!) { _update_org_oidc_connection(params: $data) { "+adminOrgOIDCConnFields+" } }", - "_update_org_oidc_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "UpdateOrgOIDCConnection", + graphql: &GraphQLRequest{ + Query: "mutation updateOrgOidcConnection($data: UpdateOrgOIDCConnectionRequest!) { _update_org_oidc_connection(params: $data) { " + adminOrgOIDCConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_update_org_oidc_connection", + responseUnwrap: "org_oidc_connection", + restResponse: func() proto.Message { return &authorizerv1.UpdateOrgOidcConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/update_org_oidc_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.UpdateOrgOidcConnection(ctx, &authorizerv1.UpdateOrgOidcConnectionRequest{ + Id: req.GetID(), Name: req.GetName(), IssuerUrl: req.GetIssuerURL(), + ClientId: req.GetClientID(), ClientSecret: req.GetClientSecret(), + Scopes: req.GetScopes(), RedirectUri: req.GetRedirectURI(), IsActive: req.GetIsActive(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1810,9 +1972,22 @@ func (c *AuthorizerAdminClient) UpdateOrgOIDCConnection(req *UpdateOrgOIDCConnec // DESTRUCTIVE: SSO logins through this connection stop working immediately. func (c *AuthorizerAdminClient) DeleteOrgOIDCConnection(req *OrgOIDCConnectionRequest) (*Response, error) { var res Response - if err := c.gqlOnly("DeleteOrgOIDCConnection", - `mutation deleteOrgOidcConnection($data: OrgOIDCConnectionRequest!) { _delete_org_oidc_connection(params: $data) { message } }`, - "_delete_org_oidc_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "DeleteOrgOIDCConnection", + graphql: &GraphQLRequest{ + Query: `mutation deleteOrgOidcConnection($data: OrgOIDCConnectionRequest!) { _delete_org_oidc_connection(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_delete_org_oidc_connection", + restResponse: func() proto.Message { return &authorizerv1.DeleteOrgOidcConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/delete_org_oidc_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.DeleteOrgOidcConnection(ctx, &authorizerv1.DeleteOrgOidcConnectionRequest{Id: req.GetID(), OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1822,9 +1997,23 @@ func (c *AuthorizerAdminClient) DeleteOrgOIDCConnection(req *OrgOIDCConnectionRe // (gql only). func (c *AuthorizerAdminClient) GetOrgOIDCConnection(req *OrgOIDCConnectionRequest) (*OrgOIDCConnection, error) { var res OrgOIDCConnection - if err := c.gqlOnly("GetOrgOIDCConnection", - "query orgOidcConnection($data: OrgOIDCConnectionRequest!) { _org_oidc_connection(params: $data) { "+adminOrgOIDCConnFields+" } }", - "_org_oidc_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "GetOrgOIDCConnection", + graphql: &GraphQLRequest{ + Query: "query orgOidcConnection($data: OrgOIDCConnectionRequest!) { _org_oidc_connection(params: $data) { " + adminOrgOIDCConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_org_oidc_connection", + responseUnwrap: "org_oidc_connection", + restResponse: func() proto.Message { return &authorizerv1.GetOrgOidcConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/org_oidc_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.GetOrgOidcConnection(ctx, &authorizerv1.GetOrgOidcConnectionRequest{Id: req.GetID(), OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1886,9 +2075,28 @@ type OrgSAMLConnectionRequest struct { // CreateOrgSAMLConnection creates an org SAML SSO connection (gql only). func (c *AuthorizerAdminClient) CreateOrgSAMLConnection(req *CreateOrgSAMLConnectionRequest) (*OrgSAMLConnection, error) { var res OrgSAMLConnection - if err := c.gqlOnly("CreateOrgSAMLConnection", - "mutation createOrgSamlConnection($data: CreateOrgSAMLConnectionRequest!) { _create_org_saml_connection(params: $data) { "+adminOrgSAMLConnFields+" } }", - "_create_org_saml_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "CreateOrgSAMLConnection", + graphql: &GraphQLRequest{ + Query: "mutation createOrgSamlConnection($data: CreateOrgSAMLConnectionRequest!) { _create_org_saml_connection(params: $data) { " + adminOrgSAMLConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_create_org_saml_connection", + responseUnwrap: "org_saml_connection", + restResponse: func() proto.Message { return &authorizerv1.CreateOrgSamlConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/create_org_saml_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.CreateOrgSamlConnection(ctx, &authorizerv1.CreateOrgSamlConnectionRequest{ + OrgId: req.GetOrgID(), Name: req.GetName(), IdpEntityId: req.GetIdpEntityID(), + IdpSsoUrl: req.GetIdpSSOURL(), IdpCertificate: req.GetIdpCertificate(), + SpEntityId: req.GetSpEntityID(), AcsUrl: req.GetAcsURL(), + AttributeMapping: req.GetAttributeMapping(), AllowIdpInitiated: req.GetAllowIdpInitiated(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1897,9 +2105,29 @@ func (c *AuthorizerAdminClient) CreateOrgSAMLConnection(req *CreateOrgSAMLConnec // UpdateOrgSAMLConnection updates an org SAML SSO connection (gql only). func (c *AuthorizerAdminClient) UpdateOrgSAMLConnection(req *UpdateOrgSAMLConnectionRequest) (*OrgSAMLConnection, error) { var res OrgSAMLConnection - if err := c.gqlOnly("UpdateOrgSAMLConnection", - "mutation updateOrgSamlConnection($data: UpdateOrgSAMLConnectionRequest!) { _update_org_saml_connection(params: $data) { "+adminOrgSAMLConnFields+" } }", - "_update_org_saml_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "UpdateOrgSAMLConnection", + graphql: &GraphQLRequest{ + Query: "mutation updateOrgSamlConnection($data: UpdateOrgSAMLConnectionRequest!) { _update_org_saml_connection(params: $data) { " + adminOrgSAMLConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_update_org_saml_connection", + responseUnwrap: "org_saml_connection", + restResponse: func() proto.Message { return &authorizerv1.UpdateOrgSamlConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/update_org_saml_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.UpdateOrgSamlConnection(ctx, &authorizerv1.UpdateOrgSamlConnectionRequest{ + Id: req.GetID(), Name: req.GetName(), IdpEntityId: req.GetIdpEntityID(), + IdpSsoUrl: req.GetIdpSSOURL(), IdpCertificate: req.GetIdpCertificate(), + SpEntityId: req.GetSpEntityID(), AcsUrl: req.GetAcsURL(), + AttributeMapping: req.GetAttributeMapping(), AllowIdpInitiated: req.GetAllowIdpInitiated(), + IsActive: req.GetIsActive(), + }) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1909,9 +2137,22 @@ func (c *AuthorizerAdminClient) UpdateOrgSAMLConnection(req *UpdateOrgSAMLConnec // DESTRUCTIVE: SSO logins through this connection stop working immediately. func (c *AuthorizerAdminClient) DeleteOrgSAMLConnection(req *OrgSAMLConnectionRequest) (*Response, error) { var res Response - if err := c.gqlOnly("DeleteOrgSAMLConnection", - `mutation deleteOrgSamlConnection($data: OrgSAMLConnectionRequest!) { _delete_org_saml_connection(params: $data) { message } }`, - "_delete_org_saml_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "DeleteOrgSAMLConnection", + graphql: &GraphQLRequest{ + Query: `mutation deleteOrgSamlConnection($data: OrgSAMLConnectionRequest!) { _delete_org_saml_connection(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_delete_org_saml_connection", + restResponse: func() proto.Message { return &authorizerv1.DeleteOrgSamlConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/delete_org_saml_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.DeleteOrgSamlConnection(ctx, &authorizerv1.DeleteOrgSamlConnectionRequest{Id: req.GetID(), OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1921,9 +2162,23 @@ func (c *AuthorizerAdminClient) DeleteOrgSAMLConnection(req *OrgSAMLConnectionRe // (gql only). func (c *AuthorizerAdminClient) GetOrgSAMLConnection(req *OrgSAMLConnectionRequest) (*OrgSAMLConnection, error) { var res OrgSAMLConnection - if err := c.gqlOnly("GetOrgSAMLConnection", - "query orgSamlConnection($data: OrgSAMLConnectionRequest!) { _org_saml_connection(params: $data) { "+adminOrgSAMLConnFields+" } }", - "_org_saml_connection", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "GetOrgSAMLConnection", + graphql: &GraphQLRequest{ + Query: "query orgSamlConnection($data: OrgSAMLConnectionRequest!) { _org_saml_connection(params: $data) { " + adminOrgSAMLConnFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_org_saml_connection", + responseUnwrap: "org_saml_connection", + restResponse: func() proto.Message { return &authorizerv1.GetOrgSamlConnectionResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/org_saml_connection", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.GetOrgSamlConnection(ctx, &authorizerv1.GetOrgSamlConnectionRequest{Id: req.GetID(), OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1962,9 +2217,22 @@ const scimEndpointResponseFragment = "scim_endpoint { " + adminScimEndpointField // CreateScimEndpoint provisions a SCIM endpoint for an organization (gql only). func (c *AuthorizerAdminClient) CreateScimEndpoint(req *CreateScimEndpointRequest) (*CreateScimEndpointResponse, error) { var res CreateScimEndpointResponse - if err := c.gqlOnly("CreateScimEndpoint", - "mutation createScimEndpoint($data: CreateScimEndpointRequest!) { _create_scim_endpoint(params: $data) { "+scimEndpointResponseFragment+" } }", - "_create_scim_endpoint", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "CreateScimEndpoint", + graphql: &GraphQLRequest{ + Query: "mutation createScimEndpoint($data: CreateScimEndpointRequest!) { _create_scim_endpoint(params: $data) { " + scimEndpointResponseFragment + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_create_scim_endpoint", + restResponse: func() proto.Message { return &authorizerv1.CreateScimEndpointResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/create_scim_endpoint", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.CreateScimEndpoint(ctx, &authorizerv1.CreateScimEndpointRequest{OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1974,9 +2242,22 @@ func (c *AuthorizerAdminClient) CreateScimEndpoint(req *CreateScimEndpointReques // token is returned ONCE; the old token stops validating. func (c *AuthorizerAdminClient) RotateScimToken(req *ScimEndpointRequest) (*CreateScimEndpointResponse, error) { var res CreateScimEndpointResponse - if err := c.gqlOnly("RotateScimToken", - "mutation rotateScimToken($data: ScimEndpointRequest!) { _rotate_scim_token(params: $data) { "+scimEndpointResponseFragment+" } }", - "_rotate_scim_token", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "RotateScimToken", + graphql: &GraphQLRequest{ + Query: "mutation rotateScimToken($data: ScimEndpointRequest!) { _rotate_scim_token(params: $data) { " + scimEndpointResponseFragment + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_rotate_scim_token", + restResponse: func() proto.Message { return &authorizerv1.CreateScimEndpointResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/rotate_scim_token", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.RotateScimToken(ctx, &authorizerv1.RotateScimTokenRequest{OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1986,9 +2267,22 @@ func (c *AuthorizerAdminClient) RotateScimToken(req *ScimEndpointRequest) (*Crea // DESTRUCTIVE: the IdP's provisioning token stops working immediately. func (c *AuthorizerAdminClient) DeleteScimEndpoint(req *ScimEndpointRequest) (*Response, error) { var res Response - if err := c.gqlOnly("DeleteScimEndpoint", - `mutation deleteScimEndpoint($data: ScimEndpointRequest!) { _delete_scim_endpoint(params: $data) { message } }`, - "_delete_scim_endpoint", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "DeleteScimEndpoint", + graphql: &GraphQLRequest{ + Query: `mutation deleteScimEndpoint($data: ScimEndpointRequest!) { _delete_scim_endpoint(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_delete_scim_endpoint", + restResponse: func() proto.Message { return &authorizerv1.DeleteScimEndpointResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/delete_scim_endpoint", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.DeleteScimEndpoint(ctx, &authorizerv1.DeleteScimEndpointRequest{OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -1998,9 +2292,23 @@ func (c *AuthorizerAdminClient) DeleteScimEndpoint(req *ScimEndpointRequest) (*R // bearer token is never returned. func (c *AuthorizerAdminClient) GetScimEndpoint(req *ScimEndpointRequest) (*ScimEndpoint, error) { var res ScimEndpoint - if err := c.gqlOnly("GetScimEndpoint", - "query scimEndpoint($data: ScimEndpointRequest!) { _scim_endpoint(params: $data) { "+adminScimEndpointFields+" } }", - "_scim_endpoint", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "GetScimEndpoint", + graphql: &GraphQLRequest{ + Query: "query scimEndpoint($data: ScimEndpointRequest!) { _scim_endpoint(params: $data) { " + adminScimEndpointFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_scim_endpoint", + responseUnwrap: "scim_endpoint", + restResponse: func() proto.Message { return &authorizerv1.GetScimEndpointResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/scim_endpoint", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.GetScimEndpoint(ctx, &authorizerv1.GetScimEndpointRequest{OrgId: req.GetOrgID()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2035,9 +2343,22 @@ const adminUserOrgFields = `organization { ` + adminOrgFields + ` } roles` // roles held per org (gql only). func (c *AuthorizerAdminClient) UserOrganizations(req *UserOrganizationsRequest) (*UserOrganizations, error) { var res UserOrganizations - if err := c.gqlOnly("UserOrganizations", - "query userOrganizations($data: UserOrganizationsRequest!) { _user_organizations(params: $data) { "+adminPaginationFields+" user_organizations { "+adminUserOrgFields+" } } }", - "_user_organizations", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "UserOrganizations", + graphql: &GraphQLRequest{ + Query: "query userOrganizations($data: UserOrganizationsRequest!) { _user_organizations(params: $data) { " + adminPaginationFields + " user_organizations { " + adminUserOrgFields + " } } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_user_organizations", + restResponse: func() proto.Message { return &authorizerv1.UserOrganizationsResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/user_organizations", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.UserOrganizations(ctx, &authorizerv1.UserOrganizationsRequest{UserId: req.GetUserID(), Pagination: req.protoPagination()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2112,9 +2433,23 @@ const adminOrgDomainFields = `domain org_id verified_at created_at updated_at` // the domain (gql only). func (c *AuthorizerAdminClient) RequestOrgDomain(req *RequestOrgDomainRequest) (*OrgDomainChallenge, error) { var res OrgDomainChallenge - if err := c.gqlOnly("RequestOrgDomain", - `mutation requestOrgDomain($data: RequestOrgDomainRequest!) { _request_org_domain(params: $data) { domain record_type record_name record_value } }`, - "_request_org_domain", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "RequestOrgDomain", + graphql: &GraphQLRequest{ + Query: `mutation requestOrgDomain($data: RequestOrgDomainRequest!) { _request_org_domain(params: $data) { domain record_type record_name record_value } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_request_org_domain", + responseUnwrap: "challenge", + restResponse: func() proto.Message { return &authorizerv1.RequestOrgDomainResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/request_org_domain", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.RequestOrgDomain(ctx, &authorizerv1.RequestOrgDomainRequest{OrgId: req.GetOrgID(), Domain: req.GetDomain()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2124,9 +2459,23 @@ func (c *AuthorizerAdminClient) RequestOrgDomain(req *RequestOrgDomainRequest) ( // domain (gql only). func (c *AuthorizerAdminClient) VerifyOrgDomain(req *VerifyOrgDomainRequest) (*OrgDomain, error) { var res OrgDomain - if err := c.gqlOnly("VerifyOrgDomain", - "mutation verifyOrgDomain($data: VerifyOrgDomainRequest!) { _verify_org_domain(params: $data) { "+adminOrgDomainFields+" } }", - "_verify_org_domain", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "VerifyOrgDomain", + graphql: &GraphQLRequest{ + Query: "mutation verifyOrgDomain($data: VerifyOrgDomainRequest!) { _verify_org_domain(params: $data) { " + adminOrgDomainFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_verify_org_domain", + responseUnwrap: "org_domain", + restResponse: func() proto.Message { return &authorizerv1.VerifyOrgDomainResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/verify_org_domain", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.VerifyOrgDomain(ctx, &authorizerv1.VerifyOrgDomainRequest{OrgId: req.GetOrgID(), Domain: req.GetDomain()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2136,9 +2485,23 @@ func (c *AuthorizerAdminClient) VerifyOrgDomain(req *VerifyOrgDomainRequest) (*O // TXT challenge. Super-admin only (gql only). func (c *AuthorizerAdminClient) AddVerifiedOrgDomain(req *AddVerifiedOrgDomainRequest) (*OrgDomain, error) { var res OrgDomain - if err := c.gqlOnly("AddVerifiedOrgDomain", - "mutation addVerifiedOrgDomain($data: AddVerifiedOrgDomainRequest!) { _add_verified_org_domain(params: $data) { "+adminOrgDomainFields+" } }", - "_add_verified_org_domain", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "AddVerifiedOrgDomain", + graphql: &GraphQLRequest{ + Query: "mutation addVerifiedOrgDomain($data: AddVerifiedOrgDomainRequest!) { _add_verified_org_domain(params: $data) { " + adminOrgDomainFields + " } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_add_verified_org_domain", + responseUnwrap: "org_domain", + restResponse: func() proto.Message { return &authorizerv1.AddVerifiedOrgDomainResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/add_verified_org_domain", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.AddVerifiedOrgDomain(ctx, &authorizerv1.AddVerifiedOrgDomainRequest{OrgId: req.GetOrgID(), Domain: req.GetDomain()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2148,9 +2511,22 @@ func (c *AuthorizerAdminClient) AddVerifiedOrgDomain(req *AddVerifiedOrgDomainRe // relying on this domain for home-realm discovery stop resolving to the org. func (c *AuthorizerAdminClient) DeleteOrgDomain(req *DeleteOrgDomainRequest) (*Response, error) { var res Response - if err := c.gqlOnly("DeleteOrgDomain", - `mutation deleteOrgDomain($data: DeleteOrgDomainRequest!) { _delete_org_domain(params: $data) { message } }`, - "_delete_org_domain", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "DeleteOrgDomain", + graphql: &GraphQLRequest{ + Query: `mutation deleteOrgDomain($data: DeleteOrgDomainRequest!) { _delete_org_domain(params: $data) { message } }`, + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_delete_org_domain", + restResponse: func() proto.Message { return &authorizerv1.DeleteOrgDomainResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/delete_org_domain", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.DeleteOrgDomain(ctx, &authorizerv1.DeleteOrgDomainRequest{Domain: req.GetDomain()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil @@ -2159,9 +2535,22 @@ func (c *AuthorizerAdminClient) DeleteOrgDomain(req *DeleteOrgDomainRequest) (*R // OrgDomains returns an organization's verified domains (gql only). func (c *AuthorizerAdminClient) OrgDomains(req *ListOrgDomainsRequest) (*OrgDomains, error) { var res OrgDomains - if err := c.gqlOnly("OrgDomains", - "query orgDomains($data: ListOrgDomainsRequest!) { _org_domains(params: $data) { "+adminPaginationFields+" org_domains { "+adminOrgDomainFields+" } } }", - "_org_domains", req, &res); err != nil { + err := c.execute(adminMethodSpec{ + name: "OrgDomains", + graphql: &GraphQLRequest{ + Query: "query orgDomains($data: ListOrgDomainsRequest!) { _org_domains(params: $data) { " + adminPaginationFields + " org_domains { " + adminOrgDomainFields + " } } }", + Variables: map[string]interface{}{"data": req}, + }, + graphqlField: "_org_domains", + restResponse: func() proto.Message { return &authorizerv1.OrgDomainsResponse{} }, + restMethod: http.MethodPost, + restPath: "/v1/admin/org_domains", + restBody: req, + grpcCall: func(ctx context.Context, cli authorizerv1.AuthorizerAdminServiceClient) (interface{}, error) { + return cli.OrgDomains(ctx, &authorizerv1.OrgDomainsRequest{OrgId: req.GetOrgID(), Pagination: req.protoPagination()}) + }, + }, &res) + if err != nil { return nil, err } return &res, nil diff --git a/go.mod b/go.mod index ff03fe8..687dd70 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/authorizerdev/authorizer-go/v2 go 1.25.5 require ( - github.com/authorizerdev/authorizer-proto-go v0.1.0 + github.com/authorizerdev/authorizer-proto-go v0.2.0-rc.0 google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 ) diff --git a/go.sum b/go.sum index c765eec..88c970c 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 h1:s6hzCXtND/ICdGPTMGk7C+/BFlr2Jg5GyH0NKf4XGXg= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= -github.com/authorizerdev/authorizer-proto-go v0.1.0 h1:oLGE2OuwCnE6Yr1tRt3fL0zh7L/HpPQfoeS4pgxszlQ= -github.com/authorizerdev/authorizer-proto-go v0.1.0/go.mod h1:cVUPv4XVeH3YeoFjfnl+ug/KlUinrGOAUZu3E+sjjHs= +github.com/authorizerdev/authorizer-proto-go v0.2.0-rc.0 h1:PQGjo4yfxU4V4NXOJj1OjbLKNxRpf3ih2eCdTMmUEkY= +github.com/authorizerdev/authorizer-proto-go v0.2.0-rc.0/go.mod h1:cVUPv4XVeH3YeoFjfnl+ug/KlUinrGOAUZu3E+sjjHs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= diff --git a/test/admin_test.go b/test/admin_test.go index 6807e15..3b76b5d 100644 --- a/test/admin_test.go +++ b/test/admin_test.go @@ -66,31 +66,34 @@ func TestAdminUsersAcrossProtocols(t *testing.T) { } } -// TestAdminMetaProtocolAvailability verifies AdminMeta works over rest+grpc and -// returns a clear error over graphql (which has no _admin_meta-shaped op here). +// TestAdminMetaProtocolAvailability verifies AdminMeta works over every +// protocol. It was rest+grpc-only in the SDK despite `_admin_meta` existing on +// the server; the SDK simply carried no query for it. func TestAdminMetaProtocolAvailability(t *testing.T) { - // rest + grpc: supported - for _, p := range []authorizer.Protocol{authorizer.ProtocolREST, authorizer.ProtocolGRPC} { + for _, p := range []authorizer.Protocol{authorizer.ProtocolGraphQL, authorizer.ProtocolREST, authorizer.ProtocolGRPC} { c := adminClient(t, p) - if _, err := c.AdminMeta(); err != nil { + res, err := c.AdminMeta() + if err != nil { t.Fatalf("[%s] AdminMeta failed: %v", p, err) } - } - - // graphql: unsupported → clear error, no network 404 - c := adminClient(t, authorizer.ProtocolGraphQL) - _, err := c.AdminMeta() - if err == nil { - t.Fatal("expected AdminMeta to error over graphql") - } - if !strings.Contains(err.Error(), "not available over graphql") { - t.Errorf("expected clear unsupported-protocol error, got %v", err) + // The proto response nests the payload under admin_meta while the + // GraphQL op returns it directly, so a missing graphqlWrap surfaces + // here as a zero-valued response rather than an error. + if res.GetAdminMeta() == nil || len(res.GetAdminMeta().GetRoles()) == 0 { + t.Errorf("[%s] AdminMeta returned %+v, want roles populated", p, res) + } } } // TestAdminGqlOnlyExtras verifies the gql-only methods error over rest+grpc. // This needs no live server: the unsupported-protocol error fires before any // network call. +// +// Only three admin operations remain graphql-only: _admin_signup, _update_env +// and _generate_jwt_keys have no proto RPC. Organizations, org members, org +// domains, org OIDC/SAML connections and SCIM endpoints DID once belong here, +// and gained RPCs plus REST bindings in server 2.4.0 -- they are now exercised +// over every protocol by TestAdminOrgSurfaceAcrossProtocols instead. func TestAdminGqlOnlyExtras(t *testing.T) { for _, p := range []authorizer.Protocol{authorizer.ProtocolREST, authorizer.ProtocolGRPC} { c := adminClient(t, p) @@ -99,32 +102,8 @@ func TestAdminGqlOnlyExtras(t *testing.T) { _, err := c.GenerateJWTKeys(&authorizer.GenerateJWTKeysRequest{Type: "HS256"}) return err }, - "CreateOrganization": func() error { - _, err := c.CreateOrganization(&authorizer.CreateOrganizationRequest{Name: "acme"}) - return err - }, - "Organizations": func() error { - _, err := c.Organizations(&authorizer.ListOrganizationsRequest{}) - return err - }, - "AddOrgMember": func() error { - _, err := c.AddOrgMember(&authorizer.AddOrgMemberRequest{OrgID: "o1", UserID: "u1"}) - return err - }, - "CreateOrgOIDCConnection": func() error { - _, err := c.CreateOrgOIDCConnection(&authorizer.CreateOrgOIDCConnectionRequest{OrgID: "o1"}) - return err - }, - "GetOrgSAMLConnection": func() error { - _, err := c.GetOrgSAMLConnection(&authorizer.OrgSAMLConnectionRequest{OrgID: authorizer.NewStringRef("o1")}) - return err - }, - "CreateScimEndpoint": func() error { - _, err := c.CreateScimEndpoint(&authorizer.CreateScimEndpointRequest{OrgID: "o1"}) - return err - }, - "RotateScimToken": func() error { - _, err := c.RotateScimToken(&authorizer.ScimEndpointRequest{OrgID: "o1"}) + "UpdateEnv": func() error { + _, err := c.UpdateEnv(&authorizer.UpdateEnvRequest{}) return err }, }