diff --git a/bin/generate_rest.sh b/bin/generate_rest.sh index 4a2625b3..18599d4f 100755 --- a/bin/generate_rest.sh +++ b/bin/generate_rest.sh @@ -27,4 +27,4 @@ swagger generate client -P rest_model_zrok.Principal -f "$zrokSpec" -c rest_clie echo "...generating js client" openapi -s specs/zrok.yml -o ui/src/api -l js -git co rest_server_zrok/configure_zrok.go +git checkout rest_server_zrok/configure_zrok.go diff --git a/cmd/zrok/generate.go b/cmd/zrok/generate.go new file mode 100644 index 00000000..4fce9500 --- /dev/null +++ b/cmd/zrok/generate.go @@ -0,0 +1,77 @@ +package main + +import ( + "fmt" + "strconv" + + "github.com/jaevor/go-nanoid" + "github.com/openziti-test-kitchen/zrok/rest_client_zrok/invite" + "github.com/openziti-test-kitchen/zrok/rest_model_zrok" + "github.com/openziti-test-kitchen/zrok/zrokdir" + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(newGenerateCommand().cmd) +} + +type generateCommand struct { + cmd *cobra.Command +} + +func newGenerateCommand() *generateCommand { + cmd := &cobra.Command{ + Use: "generate ", + Short: "Generate invite tokens (default: 5)", + Args: cobra.RangeArgs(0, 1), + } + command := &generateCommand{cmd: cmd} + cmd.Run = command.run + return command +} + +func (cmd *generateCommand) run(_ *cobra.Command, args []string) { + var iterations int64 = 5 + if len(args) == 1 { + i, err := strconv.ParseInt(args[0], 10, 64) + if err != nil { + showError("unable to parse amount", err) + } + iterations = i + } + var err error + tokens := make([]string, iterations) + for i := 0; i < int(iterations); i++ { + tokens[i], err = createToken() + if err != nil { + showError("error creating token", err) + } + } + zrok, err := zrokdir.ZrokClient(apiEndpoint) + if err != nil { + if !panicInstead { + showError("error creating zrok api client", err) + } + panic(err) + } + req := invite.NewInviteGenerateParams() + req.Body = &rest_model_zrok.InviteGenerateRequest{ + Tokens: tokens, + } + _, err = zrok.Invite.InviteGenerate(req) + if err != nil { + if !panicInstead { + showError("error creating invite tokens", err) + } + panic(err) + } + fmt.Printf("generated %d tokens\n", len(tokens)) +} + +func createToken() (string, error) { + gen, err := nanoid.CustomASCII("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", 12) + if err != nil { + return "", err + } + return gen(), nil +} diff --git a/controller/config.go b/controller/config.go index 929d72c2..c22c249f 100644 --- a/controller/config.go +++ b/controller/config.go @@ -39,6 +39,7 @@ type EmailConfig struct { type RegistrationConfig struct { EmailFrom string RegistrationUrlTemplate string + TokenStrategy string } type ZitiConfig struct { diff --git a/controller/controller.go b/controller/controller.go index 20acc667..dba22c01 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -26,7 +26,7 @@ func Run(inCfg *Config) error { api := operations.NewZrokAPI(swaggerSpec) api.KeyAuth = newZrokAuthenticator(cfg).authenticate - api.AccountInviteHandler = newInviteHandler() + api.AccountInviteHandler = newInviteHandler(cfg) api.AccountLoginHandler = account.LoginHandlerFunc(loginHandler) api.AccountRegisterHandler = newRegisterHandler() api.AccountVerifyHandler = newVerifyHandler() @@ -37,6 +37,7 @@ func Run(inCfg *Config) error { api.AdminUpdateFrontendHandler = newUpdateFrontendHandler() api.EnvironmentEnableHandler = newEnableHandler() api.EnvironmentDisableHandler = newDisableHandler() + api.InviteInviteGenerateHandler = newInviteGenerateHandler() api.MetadataGetEnvironmentDetailHandler = newEnvironmentDetailHandler() api.MetadataGetShareDetailHandler = newShareDetailHandler() api.MetadataOverviewHandler = metadata.OverviewHandlerFunc(overviewHandler) diff --git a/controller/invite.go b/controller/invite.go index 081e18cd..a45d7718 100644 --- a/controller/invite.go +++ b/controller/invite.go @@ -4,15 +4,19 @@ import ( "github.com/go-openapi/runtime/middleware" "github.com/openziti-test-kitchen/zrok/controller/store" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account" + "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/invite" "github.com/openziti-test-kitchen/zrok/util" "github.com/sirupsen/logrus" ) type inviteHandler struct { + cfg *Config } -func newInviteHandler() *inviteHandler { - return &inviteHandler{} +func newInviteHandler(cfg *Config) *inviteHandler { + return &inviteHandler{ + cfg: cfg, + } } func (self *inviteHandler) Handle(params account.InviteParams) middleware.Responder { @@ -25,17 +29,7 @@ func (self *inviteHandler) Handle(params account.InviteParams) middleware.Respon return account.NewInviteBadRequest() } logrus.Infof("received account request for email '%v'", params.Body.Email) - - token, err := createToken() - if err != nil { - logrus.Error(err) - return account.NewInviteInternalServerError() - } - ar := &store.AccountRequest{ - Token: token, - Email: params.Body.Email, - SourceAddress: params.HTTPRequest.RemoteAddr, - } + var token string tx, err := str.Begin() if err != nil { @@ -44,6 +38,31 @@ func (self *inviteHandler) Handle(params account.InviteParams) middleware.Respon } defer func() { _ = tx.Rollback() }() + if self.cfg.Registration.TokenStrategy == "store" { + invite, err := str.GetInvite(tx) + if err != nil { + logrus.Error(err) + return account.NewInviteInternalServerError() + } + invite.Status = store.INVITE_STATUS_TAKEN + if err := str.UpdateInvite(invite, tx); err != nil { + logrus.Error(err) + return account.NewInviteInternalServerError() + } + token = invite.Token + } else { + token, err = createToken() + if err != nil { + logrus.Error(err) + return account.NewInviteInternalServerError() + } + } + ar := &store.AccountRequest{ + Token: token, + Email: params.Body.Email, + SourceAddress: params.HTTPRequest.RemoteAddr, + } + if _, err := str.FindAccountWithEmail(params.Body.Email, tx); err == nil { logrus.Errorf("found account for '%v', cannot process account request", params.Body.Email) return account.NewInviteBadRequest() @@ -83,3 +102,44 @@ func (self *inviteHandler) Handle(params account.InviteParams) middleware.Respon return account.NewInviteCreated() } + +type inviteGenerateHandler struct { +} + +func newInviteGenerateHandler() *inviteGenerateHandler { + return &inviteGenerateHandler{} +} + +func (handler *inviteGenerateHandler) Handle(params invite.InviteGenerateParams) middleware.Responder { + if params.Body == nil || len(params.Body.Tokens) == 0 { + logrus.Error("missing tokens") + return invite.NewInviteGenerateBadRequest() + } + logrus.Infof("received invite generate request with %d tokens", len(params.Body.Tokens)) + + invites := make([]*store.Invite, len(params.Body.Tokens)) + for i, token := range params.Body.Tokens { + invites[i] = &store.Invite{ + Token: token, + Status: store.INVITE_STATUS_UNUSED, + } + } + tx, err := str.Begin() + if err != nil { + logrus.Error(err) + return invite.NewInviteGenerateInternalServerError() + } + defer func() { _ = tx.Rollback() }() + + if err := str.CreateInvites(invites, tx); err != nil { + logrus.Error(err) + return invite.NewInviteGenerateInternalServerError() + } + + if err := tx.Commit(); err != nil { + logrus.Errorf("error committing inviteGenerate request: %v", err) + return account.NewInviteInternalServerError() + } + + return invite.NewInviteGenerateCreated() +} diff --git a/controller/store/invites.go b/controller/store/invites.go new file mode 100644 index 00000000..257089ff --- /dev/null +++ b/controller/store/invites.go @@ -0,0 +1,61 @@ +package store + +import ( + "fmt" + "strings" + + "github.com/jmoiron/sqlx" + "github.com/pkg/errors" +) + +type Invite struct { + Model + Token string + Status string `db:"token_status"` +} + +const ( + INVITE_STATUS_UNUSED = "UNUSED" + INVITE_STATUS_TAKEN = "TAKEN" +) + +func (str *Store) CreateInvites(invites []*Invite, tx *sqlx.Tx) error { + sql := "insert into invites (token, token_status) values %s" + invs := make([]any, len(invites)*2) + queries := make([]string, len(invites)) + ct := 1 + for i, inv := range invites { + invs[i] = inv.Token + invs[i+1] = inv.Status + queries[i] = fmt.Sprintf("($%d, $%d)", ct, ct+1) + ct = ct + 2 + } + stmt, err := tx.Prepare(fmt.Sprintf(sql, strings.Join(queries, ","))) + if err != nil { + return errors.Wrap(err, "error preparing invites insert statement") + } + if _, err := stmt.Exec(invs...); err != nil { + return errors.Wrap(err, "error executing invites insert statement") + } + return nil +} + +func (str *Store) GetInvite(tx *sqlx.Tx) (*Invite, error) { + invite := &Invite{} + if err := tx.QueryRowx("select * from invites where token_status = $1 limit 1", INVITE_STATUS_UNUSED).StructScan(invite); err != nil { + return nil, errors.Wrap(err, "error getting unused invite") + } + return invite, nil +} + +func (str *Store) UpdateInvite(invite *Invite, tx *sqlx.Tx) error { + stmt, err := tx.Prepare("update invites set token = $1, token_status = $2") + if err != nil { + return errors.Wrap(err, "error perparing invites update statement") + } + _, err = stmt.Exec(invite.Token, invite.Status) + if err != nil { + return errors.Wrap(err, "error executing invites update statement") + } + return nil +} diff --git a/controller/store/sql/postgresql/005_v0_3_0_invites.sql b/controller/store/sql/postgresql/005_v0_3_0_invites.sql new file mode 100644 index 00000000..92894566 --- /dev/null +++ b/controller/store/sql/postgresql/005_v0_3_0_invites.sql @@ -0,0 +1,16 @@ +-- +migrate Up + +-- +-- invites +--- + +create table invites ( + id serial primary key, + token varchar(32) not null unique, + token_status varchar(1024) not null unique, + created_at timestamp not null default(current_timestamp), + updated_at timestamp not null default(current_timestamp), + + constraint chk_token check(token <> ''), + constraint chk_status check(token_status <> '') +); \ No newline at end of file diff --git a/controller/store/sql/sqlite3/005_v0_3_0_invites.sql b/controller/store/sql/sqlite3/005_v0_3_0_invites.sql new file mode 100644 index 00000000..92894566 --- /dev/null +++ b/controller/store/sql/sqlite3/005_v0_3_0_invites.sql @@ -0,0 +1,16 @@ +-- +migrate Up + +-- +-- invites +--- + +create table invites ( + id serial primary key, + token varchar(32) not null unique, + token_status varchar(1024) not null unique, + created_at timestamp not null default(current_timestamp), + updated_at timestamp not null default(current_timestamp), + + constraint chk_token check(token <> ''), + constraint chk_status check(token_status <> '') +); \ No newline at end of file diff --git a/go.mod b/go.mod index c6a3959a..b0bba662 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/iancoleman/strcase v0.2.0 github.com/influxdata/influxdb-client-go/v2 v2.11.0 github.com/jaevor/go-nanoid v1.3.0 + github.com/jedib0t/go-pretty/v6 v6.4.3 github.com/jessevdk/go-flags v1.5.0 github.com/jmoiron/sqlx v1.3.5 github.com/lib/pq v1.10.0 @@ -33,7 +34,6 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.1 - github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 github.com/wneessen/go-mail v0.2.7 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 @@ -58,7 +58,6 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect - github.com/jedib0t/go-pretty/v6 v6.4.3 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect diff --git a/go.sum b/go.sum index a0feeb01..e3241d8d 100644 --- a/go.sum +++ b/go.sum @@ -520,8 +520,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569 h1:xzABM9let0HLLqFypcxvLmlvEciCHL7+Lv+4vwZqecI= -github.com/teris-io/shortid v0.0.0-20220617161101-71ec9f2aa569/go.mod h1:2Ly+NIftZN4de9zRmENdYbvPQeaVIYKWpLFStLFEBgI= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= diff --git a/rest_client_zrok/invite/invite_client.go b/rest_client_zrok/invite/invite_client.go new file mode 100644 index 00000000..f64b51ab --- /dev/null +++ b/rest_client_zrok/invite/invite_client.go @@ -0,0 +1,79 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// New creates a new invite API client. +func New(transport runtime.ClientTransport, formats strfmt.Registry) ClientService { + return &Client{transport: transport, formats: formats} +} + +/* +Client for invite API +*/ +type Client struct { + transport runtime.ClientTransport + formats strfmt.Registry +} + +// ClientOption is the option for Client methods +type ClientOption func(*runtime.ClientOperation) + +// ClientService is the interface for Client methods +type ClientService interface { + InviteGenerate(params *InviteGenerateParams, opts ...ClientOption) (*InviteGenerateCreated, error) + + SetTransport(transport runtime.ClientTransport) +} + +/* +InviteGenerate invite generate API +*/ +func (a *Client) InviteGenerate(params *InviteGenerateParams, opts ...ClientOption) (*InviteGenerateCreated, error) { + // TODO: Validate the params before sending + if params == nil { + params = NewInviteGenerateParams() + } + op := &runtime.ClientOperation{ + ID: "inviteGenerate", + Method: "POST", + PathPattern: "/invite/generate", + ProducesMediaTypes: []string{"application/zrok.v1+json"}, + ConsumesMediaTypes: []string{"application/zrok.v1+json"}, + Schemes: []string{"http"}, + Params: params, + Reader: &InviteGenerateReader{formats: a.formats}, + Context: params.Context, + Client: params.HTTPClient, + } + for _, opt := range opts { + opt(op) + } + + result, err := a.transport.Submit(op) + if err != nil { + return nil, err + } + success, ok := result.(*InviteGenerateCreated) + if ok { + return success, nil + } + // unexpected success response + // safeguard: normally, absent a default response, unknown success responses return an error above: so this is a codegen issue + msg := fmt.Sprintf("unexpected success response for inviteGenerate: API contract not enforced by server. Client expected to get an error, but got: %T", result) + panic(msg) +} + +// SetTransport changes the transport on the client +func (a *Client) SetTransport(transport runtime.ClientTransport) { + a.transport = transport +} diff --git a/rest_client_zrok/invite/invite_generate_parameters.go b/rest_client_zrok/invite/invite_generate_parameters.go new file mode 100644 index 00000000..e585966d --- /dev/null +++ b/rest_client_zrok/invite/invite_generate_parameters.go @@ -0,0 +1,150 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "net/http" + "time" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + cr "github.com/go-openapi/runtime/client" + "github.com/go-openapi/strfmt" + + "github.com/openziti-test-kitchen/zrok/rest_model_zrok" +) + +// NewInviteGenerateParams creates a new InviteGenerateParams object, +// with the default timeout for this client. +// +// Default values are not hydrated, since defaults are normally applied by the API server side. +// +// To enforce default values in parameter, use SetDefaults or WithDefaults. +func NewInviteGenerateParams() *InviteGenerateParams { + return &InviteGenerateParams{ + timeout: cr.DefaultTimeout, + } +} + +// NewInviteGenerateParamsWithTimeout creates a new InviteGenerateParams object +// with the ability to set a timeout on a request. +func NewInviteGenerateParamsWithTimeout(timeout time.Duration) *InviteGenerateParams { + return &InviteGenerateParams{ + timeout: timeout, + } +} + +// NewInviteGenerateParamsWithContext creates a new InviteGenerateParams object +// with the ability to set a context for a request. +func NewInviteGenerateParamsWithContext(ctx context.Context) *InviteGenerateParams { + return &InviteGenerateParams{ + Context: ctx, + } +} + +// NewInviteGenerateParamsWithHTTPClient creates a new InviteGenerateParams object +// with the ability to set a custom HTTPClient for a request. +func NewInviteGenerateParamsWithHTTPClient(client *http.Client) *InviteGenerateParams { + return &InviteGenerateParams{ + HTTPClient: client, + } +} + +/* +InviteGenerateParams contains all the parameters to send to the API endpoint + + for the invite generate operation. + + Typically these are written to a http.Request. +*/ +type InviteGenerateParams struct { + + // Body. + Body *rest_model_zrok.InviteGenerateRequest + + timeout time.Duration + Context context.Context + HTTPClient *http.Client +} + +// WithDefaults hydrates default values in the invite generate params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *InviteGenerateParams) WithDefaults() *InviteGenerateParams { + o.SetDefaults() + return o +} + +// SetDefaults hydrates default values in the invite generate params (not the query body). +// +// All values with no default are reset to their zero value. +func (o *InviteGenerateParams) SetDefaults() { + // no default values defined for this parameter +} + +// WithTimeout adds the timeout to the invite generate params +func (o *InviteGenerateParams) WithTimeout(timeout time.Duration) *InviteGenerateParams { + o.SetTimeout(timeout) + return o +} + +// SetTimeout adds the timeout to the invite generate params +func (o *InviteGenerateParams) SetTimeout(timeout time.Duration) { + o.timeout = timeout +} + +// WithContext adds the context to the invite generate params +func (o *InviteGenerateParams) WithContext(ctx context.Context) *InviteGenerateParams { + o.SetContext(ctx) + return o +} + +// SetContext adds the context to the invite generate params +func (o *InviteGenerateParams) SetContext(ctx context.Context) { + o.Context = ctx +} + +// WithHTTPClient adds the HTTPClient to the invite generate params +func (o *InviteGenerateParams) WithHTTPClient(client *http.Client) *InviteGenerateParams { + o.SetHTTPClient(client) + return o +} + +// SetHTTPClient adds the HTTPClient to the invite generate params +func (o *InviteGenerateParams) SetHTTPClient(client *http.Client) { + o.HTTPClient = client +} + +// WithBody adds the body to the invite generate params +func (o *InviteGenerateParams) WithBody(body *rest_model_zrok.InviteGenerateRequest) *InviteGenerateParams { + o.SetBody(body) + return o +} + +// SetBody adds the body to the invite generate params +func (o *InviteGenerateParams) SetBody(body *rest_model_zrok.InviteGenerateRequest) { + o.Body = body +} + +// WriteToRequest writes these params to a swagger request +func (o *InviteGenerateParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error { + + if err := r.SetTimeout(o.timeout); err != nil { + return err + } + var res []error + if o.Body != nil { + if err := r.SetBodyParam(o.Body); err != nil { + return err + } + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_client_zrok/invite/invite_generate_responses.go b/rest_client_zrok/invite/invite_generate_responses.go new file mode 100644 index 00000000..763a70df --- /dev/null +++ b/rest_client_zrok/invite/invite_generate_responses.go @@ -0,0 +1,197 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "fmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/strfmt" +) + +// InviteGenerateReader is a Reader for the InviteGenerate structure. +type InviteGenerateReader struct { + formats strfmt.Registry +} + +// ReadResponse reads a server response into the received o. +func (o *InviteGenerateReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { + switch response.Code() { + case 201: + result := NewInviteGenerateCreated() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return result, nil + case 400: + result := NewInviteGenerateBadRequest() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + case 500: + result := NewInviteGenerateInternalServerError() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result + default: + return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code()) + } +} + +// NewInviteGenerateCreated creates a InviteGenerateCreated with default headers values +func NewInviteGenerateCreated() *InviteGenerateCreated { + return &InviteGenerateCreated{} +} + +/* +InviteGenerateCreated describes a response with status code 201, with default header values. + +invitation tokens created +*/ +type InviteGenerateCreated struct { +} + +// IsSuccess returns true when this invite generate created response has a 2xx status code +func (o *InviteGenerateCreated) IsSuccess() bool { + return true +} + +// IsRedirect returns true when this invite generate created response has a 3xx status code +func (o *InviteGenerateCreated) IsRedirect() bool { + return false +} + +// IsClientError returns true when this invite generate created response has a 4xx status code +func (o *InviteGenerateCreated) IsClientError() bool { + return false +} + +// IsServerError returns true when this invite generate created response has a 5xx status code +func (o *InviteGenerateCreated) IsServerError() bool { + return false +} + +// IsCode returns true when this invite generate created response a status code equal to that given +func (o *InviteGenerateCreated) IsCode(code int) bool { + return code == 201 +} + +func (o *InviteGenerateCreated) Error() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateCreated ", 201) +} + +func (o *InviteGenerateCreated) String() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateCreated ", 201) +} + +func (o *InviteGenerateCreated) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewInviteGenerateBadRequest creates a InviteGenerateBadRequest with default headers values +func NewInviteGenerateBadRequest() *InviteGenerateBadRequest { + return &InviteGenerateBadRequest{} +} + +/* +InviteGenerateBadRequest describes a response with status code 400, with default header values. + +invitation tokens not created +*/ +type InviteGenerateBadRequest struct { +} + +// IsSuccess returns true when this invite generate bad request response has a 2xx status code +func (o *InviteGenerateBadRequest) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this invite generate bad request response has a 3xx status code +func (o *InviteGenerateBadRequest) IsRedirect() bool { + return false +} + +// IsClientError returns true when this invite generate bad request response has a 4xx status code +func (o *InviteGenerateBadRequest) IsClientError() bool { + return true +} + +// IsServerError returns true when this invite generate bad request response has a 5xx status code +func (o *InviteGenerateBadRequest) IsServerError() bool { + return false +} + +// IsCode returns true when this invite generate bad request response a status code equal to that given +func (o *InviteGenerateBadRequest) IsCode(code int) bool { + return code == 400 +} + +func (o *InviteGenerateBadRequest) Error() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateBadRequest ", 400) +} + +func (o *InviteGenerateBadRequest) String() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateBadRequest ", 400) +} + +func (o *InviteGenerateBadRequest) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + +// NewInviteGenerateInternalServerError creates a InviteGenerateInternalServerError with default headers values +func NewInviteGenerateInternalServerError() *InviteGenerateInternalServerError { + return &InviteGenerateInternalServerError{} +} + +/* +InviteGenerateInternalServerError describes a response with status code 500, with default header values. + +internal server error +*/ +type InviteGenerateInternalServerError struct { +} + +// IsSuccess returns true when this invite generate internal server error response has a 2xx status code +func (o *InviteGenerateInternalServerError) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this invite generate internal server error response has a 3xx status code +func (o *InviteGenerateInternalServerError) IsRedirect() bool { + return false +} + +// IsClientError returns true when this invite generate internal server error response has a 4xx status code +func (o *InviteGenerateInternalServerError) IsClientError() bool { + return false +} + +// IsServerError returns true when this invite generate internal server error response has a 5xx status code +func (o *InviteGenerateInternalServerError) IsServerError() bool { + return true +} + +// IsCode returns true when this invite generate internal server error response a status code equal to that given +func (o *InviteGenerateInternalServerError) IsCode(code int) bool { + return code == 500 +} + +func (o *InviteGenerateInternalServerError) Error() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateInternalServerError ", 500) +} + +func (o *InviteGenerateInternalServerError) String() string { + return fmt.Sprintf("[POST /invite/generate][%d] inviteGenerateInternalServerError ", 500) +} + +func (o *InviteGenerateInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} diff --git a/rest_client_zrok/zrok_client.go b/rest_client_zrok/zrok_client.go index 2aaac3fd..379b6717 100644 --- a/rest_client_zrok/zrok_client.go +++ b/rest_client_zrok/zrok_client.go @@ -13,6 +13,7 @@ import ( "github.com/openziti-test-kitchen/zrok/rest_client_zrok/account" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/admin" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/environment" + "github.com/openziti-test-kitchen/zrok/rest_client_zrok/invite" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/metadata" "github.com/openziti-test-kitchen/zrok/rest_client_zrok/share" ) @@ -62,6 +63,7 @@ func New(transport runtime.ClientTransport, formats strfmt.Registry) *Zrok { cli.Account = account.New(transport, formats) cli.Admin = admin.New(transport, formats) cli.Environment = environment.New(transport, formats) + cli.Invite = invite.New(transport, formats) cli.Metadata = metadata.New(transport, formats) cli.Share = share.New(transport, formats) return cli @@ -114,6 +116,8 @@ type Zrok struct { Environment environment.ClientService + Invite invite.ClientService + Metadata metadata.ClientService Share share.ClientService @@ -127,6 +131,7 @@ func (c *Zrok) SetTransport(transport runtime.ClientTransport) { c.Account.SetTransport(transport) c.Admin.SetTransport(transport) c.Environment.SetTransport(transport) + c.Invite.SetTransport(transport) c.Metadata.SetTransport(transport) c.Share.SetTransport(transport) } diff --git a/rest_model_zrok/invite_generate_request.go b/rest_model_zrok/invite_generate_request.go new file mode 100644 index 00000000..f662c3d5 --- /dev/null +++ b/rest_model_zrok/invite_generate_request.go @@ -0,0 +1,50 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package rest_model_zrok + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// InviteGenerateRequest invite generate request +// +// swagger:model inviteGenerateRequest +type InviteGenerateRequest struct { + + // tokens + Tokens []string `json:"tokens"` +} + +// Validate validates this invite generate request +func (m *InviteGenerateRequest) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this invite generate request based on context it is used +func (m *InviteGenerateRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *InviteGenerateRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *InviteGenerateRequest) UnmarshalBinary(b []byte) error { + var res InviteGenerateRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 704194d9..b8466732 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -431,6 +431,34 @@ func init() { } } }, + "/invite/generate": { + "post": { + "tags": [ + "invite" + ], + "operationId": "inviteGenerate", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/inviteGenerateRequest" + } + } + ], + "responses": { + "201": { + "description": "invitation tokens created" + }, + "400": { + "description": "invitation tokens not created" + }, + "500": { + "description": "internal server error" + } + } + } + }, "/login": { "post": { "tags": [ @@ -859,6 +887,17 @@ func init() { "errorMessage": { "type": "string" }, + "inviteGenerateRequest": { + "type": "object", + "properties": { + "tokens": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "inviteRequest": { "type": "object", "properties": { @@ -1549,6 +1588,34 @@ func init() { } } }, + "/invite/generate": { + "post": { + "tags": [ + "invite" + ], + "operationId": "inviteGenerate", + "parameters": [ + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/inviteGenerateRequest" + } + } + ], + "responses": { + "201": { + "description": "invitation tokens created" + }, + "400": { + "description": "invitation tokens not created" + }, + "500": { + "description": "internal server error" + } + } + } + }, "/login": { "post": { "tags": [ @@ -1977,6 +2044,17 @@ func init() { "errorMessage": { "type": "string" }, + "inviteGenerateRequest": { + "type": "object", + "properties": { + "tokens": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, "inviteRequest": { "type": "object", "properties": { diff --git a/rest_server_zrok/operations/invite/invite_generate.go b/rest_server_zrok/operations/invite/invite_generate.go new file mode 100644 index 00000000..7718776c --- /dev/null +++ b/rest_server_zrok/operations/invite/invite_generate.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// InviteGenerateHandlerFunc turns a function with the right signature into a invite generate handler +type InviteGenerateHandlerFunc func(InviteGenerateParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn InviteGenerateHandlerFunc) Handle(params InviteGenerateParams) middleware.Responder { + return fn(params) +} + +// InviteGenerateHandler interface for that can handle valid invite generate params +type InviteGenerateHandler interface { + Handle(InviteGenerateParams) middleware.Responder +} + +// NewInviteGenerate creates a new http.Handler for the invite generate operation +func NewInviteGenerate(ctx *middleware.Context, handler InviteGenerateHandler) *InviteGenerate { + return &InviteGenerate{Context: ctx, Handler: handler} +} + +/* + InviteGenerate swagger:route POST /invite/generate invite inviteGenerate + +InviteGenerate invite generate API +*/ +type InviteGenerate struct { + Context *middleware.Context + Handler InviteGenerateHandler +} + +func (o *InviteGenerate) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewInviteGenerateParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/rest_server_zrok/operations/invite/invite_generate_parameters.go b/rest_server_zrok/operations/invite/invite_generate_parameters.go new file mode 100644 index 00000000..df0d20cc --- /dev/null +++ b/rest_server_zrok/operations/invite/invite_generate_parameters.go @@ -0,0 +1,76 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/validate" + + "github.com/openziti-test-kitchen/zrok/rest_model_zrok" +) + +// NewInviteGenerateParams creates a new InviteGenerateParams object +// +// There are no default values defined in the spec. +func NewInviteGenerateParams() InviteGenerateParams { + + return InviteGenerateParams{} +} + +// InviteGenerateParams contains all the bound params for the invite generate operation +// typically these are obtained from a http.Request +// +// swagger:parameters inviteGenerate +type InviteGenerateParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + In: body + */ + Body *rest_model_zrok.InviteGenerateRequest +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewInviteGenerateParams() beforehand. +func (o *InviteGenerateParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body rest_model_zrok.InviteGenerateRequest + if err := route.Consumer.Consume(r.Body, &body); err != nil { + res = append(res, errors.NewParseError("body", "body", "", err)) + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/rest_server_zrok/operations/invite/invite_generate_responses.go b/rest_server_zrok/operations/invite/invite_generate_responses.go new file mode 100644 index 00000000..5c08ddc6 --- /dev/null +++ b/rest_server_zrok/operations/invite/invite_generate_responses.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" +) + +// InviteGenerateCreatedCode is the HTTP code returned for type InviteGenerateCreated +const InviteGenerateCreatedCode int = 201 + +/* +InviteGenerateCreated invitation tokens created + +swagger:response inviteGenerateCreated +*/ +type InviteGenerateCreated struct { +} + +// NewInviteGenerateCreated creates InviteGenerateCreated with default headers values +func NewInviteGenerateCreated() *InviteGenerateCreated { + + return &InviteGenerateCreated{} +} + +// WriteResponse to the client +func (o *InviteGenerateCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(201) +} + +// InviteGenerateBadRequestCode is the HTTP code returned for type InviteGenerateBadRequest +const InviteGenerateBadRequestCode int = 400 + +/* +InviteGenerateBadRequest invitation tokens not created + +swagger:response inviteGenerateBadRequest +*/ +type InviteGenerateBadRequest struct { +} + +// NewInviteGenerateBadRequest creates InviteGenerateBadRequest with default headers values +func NewInviteGenerateBadRequest() *InviteGenerateBadRequest { + + return &InviteGenerateBadRequest{} +} + +// WriteResponse to the client +func (o *InviteGenerateBadRequest) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(400) +} + +// InviteGenerateInternalServerErrorCode is the HTTP code returned for type InviteGenerateInternalServerError +const InviteGenerateInternalServerErrorCode int = 500 + +/* +InviteGenerateInternalServerError internal server error + +swagger:response inviteGenerateInternalServerError +*/ +type InviteGenerateInternalServerError struct { +} + +// NewInviteGenerateInternalServerError creates InviteGenerateInternalServerError with default headers values +func NewInviteGenerateInternalServerError() *InviteGenerateInternalServerError { + + return &InviteGenerateInternalServerError{} +} + +// WriteResponse to the client +func (o *InviteGenerateInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(500) +} diff --git a/rest_server_zrok/operations/invite/invite_generate_urlbuilder.go b/rest_server_zrok/operations/invite/invite_generate_urlbuilder.go new file mode 100644 index 00000000..ea9a40f1 --- /dev/null +++ b/rest_server_zrok/operations/invite/invite_generate_urlbuilder.go @@ -0,0 +1,87 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package invite + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// InviteGenerateURL generates an URL for the invite generate operation +type InviteGenerateURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *InviteGenerateURL) WithBasePath(bp string) *InviteGenerateURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *InviteGenerateURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *InviteGenerateURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/invite/generate" + + _basePath := o._basePath + if _basePath == "" { + _basePath = "/api/v1" + } + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *InviteGenerateURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *InviteGenerateURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *InviteGenerateURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on InviteGenerateURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on InviteGenerateURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *InviteGenerateURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/rest_server_zrok/operations/zrok_api.go b/rest_server_zrok/operations/zrok_api.go index ee0ced95..f1304d2d 100644 --- a/rest_server_zrok/operations/zrok_api.go +++ b/rest_server_zrok/operations/zrok_api.go @@ -23,6 +23,7 @@ import ( "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/admin" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/environment" + "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/invite" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/share" ) @@ -76,6 +77,9 @@ func NewZrokAPI(spec *loads.Document) *ZrokAPI { AccountInviteHandler: account.InviteHandlerFunc(func(params account.InviteParams) middleware.Responder { return middleware.NotImplemented("operation account.Invite has not yet been implemented") }), + InviteInviteGenerateHandler: invite.InviteGenerateHandlerFunc(func(params invite.InviteGenerateParams) middleware.Responder { + return middleware.NotImplemented("operation invite.InviteGenerate has not yet been implemented") + }), AdminListFrontendsHandler: admin.ListFrontendsHandlerFunc(func(params admin.ListFrontendsParams, principal *rest_model_zrok.Principal) middleware.Responder { return middleware.NotImplemented("operation admin.ListFrontends has not yet been implemented") }), @@ -177,6 +181,8 @@ type ZrokAPI struct { MetadataGetShareDetailHandler metadata.GetShareDetailHandler // AccountInviteHandler sets the operation handler for the invite operation AccountInviteHandler account.InviteHandler + // InviteInviteGenerateHandler sets the operation handler for the invite generate operation + InviteInviteGenerateHandler invite.InviteGenerateHandler // AdminListFrontendsHandler sets the operation handler for the list frontends operation AdminListFrontendsHandler admin.ListFrontendsHandler // AccountLoginHandler sets the operation handler for the login operation @@ -307,6 +313,9 @@ func (o *ZrokAPI) Validate() error { if o.AccountInviteHandler == nil { unregistered = append(unregistered, "account.InviteHandler") } + if o.InviteInviteGenerateHandler == nil { + unregistered = append(unregistered, "invite.InviteGenerateHandler") + } if o.AdminListFrontendsHandler == nil { unregistered = append(unregistered, "admin.ListFrontendsHandler") } @@ -475,6 +484,10 @@ func (o *ZrokAPI) initHandlerCache() { o.handlers["POST"] = make(map[string]http.Handler) } o.handlers["POST"]["/invite"] = account.NewInvite(o.context, o.AccountInviteHandler) + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"]["/invite/generate"] = invite.NewInviteGenerate(o.context, o.InviteInviteGenerateHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } diff --git a/specs/zrok.yml b/specs/zrok.yml index fe3512e6..3e134d63 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -446,6 +446,27 @@ paths: schema: $ref: "#/definitions/errorMessage" + # + # invite + # + /invite/generate: + post: + tags: + - invite + operationId: inviteGenerate + parameters: + - name: body + in: body + schema: + $ref: "#/definitions/inviteGenerateRequest" + responses: + 201: + description: invitation tokens created + 400: + description: invitation tokens not created + 500: + description: internal server error + definitions: accessRequest: type: object @@ -740,6 +761,14 @@ definitions: version: type: string + inviteGenerateRequest: + type: object + properties: + tokens: + type: array + items: + type: string + produces: - application/zrok.v1+json consumes: diff --git a/ui/src/api/invite.js b/ui/src/api/invite.js new file mode 100644 index 00000000..4dd4704f --- /dev/null +++ b/ui/src/api/invite.js @@ -0,0 +1,24 @@ +/** @module invite */ +// Auto-generated, edits will be overwritten +import * as gateway from './gateway' + +/** + * @param {object} options Optional options + * @param {module:types.inviteGenerateRequest} [options.body] + * @return {Promise} invitation tokens created + */ +export function inviteGenerate(options) { + if (!options) options = {} + const parameters = { + body: { + body: options.body + } + } + return gateway.request(inviteGenerateOperation, parameters) +} + +const inviteGenerateOperation = { + path: '/invite/generate', + contentTypes: ['application/zrok.v1+json'], + method: 'post' +} diff --git a/ui/src/api/types.js b/ui/src/api/types.js index a7f66df0..92c482a1 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -230,3 +230,10 @@ * * @property {string} email */ + +/** + * @typedef inviteGenerateRequest + * @memberof module:types + * + * @property {string[]} tokens + */