diff --git a/agent/sharePrivate.go b/agent/sharePrivate.go index d5f24155..c13b9fb1 100644 --- a/agent/sharePrivate.go +++ b/agent/sharePrivate.go @@ -47,8 +47,8 @@ func (a *Agent) SharePrivate(req *SharePrivateRequest) (shareToken string, err e } shr.insecure = req.Insecure - if req.Closed { - shrCmd = append(shrCmd, "--closed") + if !req.Closed { + shrCmd = append(shrCmd, "--open") } shr.closed = req.Closed diff --git a/agent/sharePublic.go b/agent/sharePublic.go index 5db0a521..4127f08d 100644 --- a/agent/sharePublic.go +++ b/agent/sharePublic.go @@ -71,8 +71,8 @@ func (a *Agent) SharePublic(req *SharePublicRequest) (shareToken string, fronten shrCmd = append(shrCmd, "--oauth-check-interval", req.OauthCheckInterval) } - if req.Closed { - shrCmd = append(shrCmd, "--closed") + if !req.Closed { + shrCmd = append(shrCmd, "--open") } shr.closed = req.Closed diff --git a/controller/agentRemoteShare.go b/controller/agentRemoteShare.go new file mode 100644 index 00000000..ecb5c5d7 --- /dev/null +++ b/controller/agentRemoteShare.go @@ -0,0 +1,125 @@ +package controller + +import ( + "context" + "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/agent/agentGrpc" + "github.com/openziti/zrok/controller/agentController" + "github.com/openziti/zrok/rest_model_zrok" + "github.com/openziti/zrok/rest_server_zrok/operations/agent" + "github.com/sirupsen/logrus" +) + +type agentRemoteShareHandler struct{} + +func newAgentRemoteShareHandler() *agentRemoteShareHandler { + return &agentRemoteShareHandler{} +} + +func (h *agentRemoteShareHandler) Handle(params agent.RemoteShareParams, principal *rest_model_zrok.Principal) middleware.Responder { + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction for '%v': %v", principal.Email, err) + return agent.NewRemoteShareInternalServerError() + } + defer trx.Rollback() + + env, err := str.FindEnvironmentForAccount(params.Body.EnvZID, int(principal.ID), trx) + if err != nil { + logrus.Errorf("error finding environment '%v' for '%v': %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareUnauthorized() + } + + ae, err := str.FindAgentEnrollmentForEnvironment(env.Id, trx) + if err != nil { + logrus.Errorf("error finding agent enrollment for environment '%v' (%v): %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareBadGateway() + } + _ = trx.Rollback() + + acli, aconn, err := agentController.NewAgentClient(ae.Token, cfg.AgentController) + if err != nil { + logrus.Errorf("error creating agent client for '%v' (%v): %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareInternalServerError() + } + defer aconn.Close() + + out := &agent.RemoteShareOKBody{} + switch params.Body.ShareMode { + case "public": + token, frontendEndpoints, err := h.publicShare(params, acli) + if err != nil { + logrus.Errorf("error creating public remote agent share for '%v' (%v): %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareBadGateway() + } + out.Token = token + out.FrontendEndpoints = frontendEndpoints + + case "private": + token, err := h.privateShare(params, acli) + if err != nil { + logrus.Errorf("error creating private remote agent share for '%v' (%v): %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareBadGateway() + } + out.Token = token + + case "reserved": + token, err := h.reservedShare(params, acli) + if err != nil { + logrus.Errorf("error creating reserved remote agent share for '%v' (%v): %v", params.Body.EnvZID, principal.Email, err) + return agent.NewRemoteShareBadGateway() + } + out.Token = token + } + + return agent.NewRemoteShareOK().WithPayload(out) +} + +func (h *agentRemoteShareHandler) publicShare(params agent.RemoteShareParams, client agentGrpc.AgentClient) (token string, frontendEndpoints []string, err error) { + req := &agentGrpc.SharePublicRequest{ + Target: params.Body.Target, + BasicAuth: params.Body.BasicAuth, + FrontendSelection: params.Body.FrontendSelection, + BackendMode: params.Body.BackendMode, + Insecure: params.Body.Insecure, + OauthProvider: params.Body.OauthProvider, + OauthEmailAddressPatterns: params.Body.OauthEmailAddressPatterns, + OauthCheckInterval: params.Body.OauthCheckInterval, + Closed: !params.Body.Open, + AccessGrants: params.Body.AccessGrants, + } + resp, err := client.SharePublic(context.Background(), req) + if err != nil { + return "", nil, err + } + logrus.Infof("got token '%v'", resp.Token) + return resp.Token, resp.FrontendEndpoints, nil +} + +func (h *agentRemoteShareHandler) privateShare(params agent.RemoteShareParams, client agentGrpc.AgentClient) (token string, err error) { + req := &agentGrpc.SharePrivateRequest{ + Target: params.Body.Target, + BackendMode: params.Body.BackendMode, + Insecure: params.Body.Insecure, + Closed: !params.Body.Open, + AccessGrants: params.Body.AccessGrants, + } + resp, err := client.SharePrivate(context.Background(), req) + if err != nil { + return "", err + } + return resp.Token, nil +} + +func (h *agentRemoteShareHandler) reservedShare(params agent.RemoteShareParams, client agentGrpc.AgentClient) (token string, err error) { + req := &agentGrpc.ShareReservedRequest{ + Token: params.Body.Token, + OverrideEndpoint: params.Body.Target, + Insecure: params.Body.Insecure, + } + resp, err := client.ShareReserved(context.Background(), req) + if err != nil { + return "", err + } + return resp.Token, nil +} diff --git a/controller/controller.go b/controller/controller.go index a9ea45e0..f4ae0907 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -68,6 +68,7 @@ func Run(inCfg *config.Config) error { if cfg.AgentController != nil { api.AgentEnrollHandler = newAgentEnrollHandler() api.AgentPingHandler = newAgentPingHandler() + api.AgentRemoteShareHandler = newAgentRemoteShareHandler() api.AgentUnenrollHandler = newAgentUnenrollHandler() } api.EnvironmentEnableHandler = newEnableHandler() diff --git a/rest_client_zrok/agent/remote_share_responses.go b/rest_client_zrok/agent/remote_share_responses.go index 75af5880..d1c0bc4a 100644 --- a/rest_client_zrok/agent/remote_share_responses.go +++ b/rest_client_zrok/agent/remote_share_responses.go @@ -309,6 +309,9 @@ type RemoteShareBody struct { // basic auth BasicAuth []string `json:"basicAuth"` + // env z Id + EnvZID string `json:"envZId,omitempty"` + // frontend selection FrontendSelection []string `json:"frontendSelection"` @@ -328,6 +331,7 @@ type RemoteShareBody struct { Open bool `json:"open,omitempty"` // share mode + // Enum: ["public","private","reserved"] ShareMode string `json:"shareMode,omitempty"` // target @@ -345,6 +349,10 @@ func (o *RemoteShareBody) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := o.validateShareMode(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -411,6 +419,51 @@ func (o *RemoteShareBody) validateBackendMode(formats strfmt.Registry) error { return nil } +var remoteShareBodyTypeShareModePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["public","private","reserved"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + remoteShareBodyTypeShareModePropEnum = append(remoteShareBodyTypeShareModePropEnum, v) + } +} + +const ( + + // RemoteShareBodyShareModePublic captures enum value "public" + RemoteShareBodyShareModePublic string = "public" + + // RemoteShareBodyShareModePrivate captures enum value "private" + RemoteShareBodyShareModePrivate string = "private" + + // RemoteShareBodyShareModeReserved captures enum value "reserved" + RemoteShareBodyShareModeReserved string = "reserved" +) + +// prop value enum +func (o *RemoteShareBody) validateShareModeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, remoteShareBodyTypeShareModePropEnum, true); err != nil { + return err + } + return nil +} + +func (o *RemoteShareBody) validateShareMode(formats strfmt.Registry) error { + if swag.IsZero(o.ShareMode) { // not required + return nil + } + + // value enum + if err := o.validateShareModeEnum("body"+"."+"shareMode", "body", o.ShareMode); err != nil { + return err + } + + return nil +} + // ContextValidate validates this remote share body based on context it is used func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil diff --git a/rest_client_zrok/agent/unenroll_responses.go b/rest_client_zrok/agent/unenroll_responses.go index 2832e1b4..be78d236 100644 --- a/rest_client_zrok/agent/unenroll_responses.go +++ b/rest_client_zrok/agent/unenroll_responses.go @@ -227,7 +227,7 @@ func NewUnenrollInternalServerError() *UnenrollInternalServerError { /* UnenrollInternalServerError describes a response with status code 500, with default header values. -internal server er +UnenrollInternalServerError unenroll internal server error */ type UnenrollInternalServerError struct { } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 1b719010..acd7a83f 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -321,6 +321,9 @@ func init() { "type": "string" } }, + "envZId": { + "type": "string" + }, "frontendSelection": { "type": "array", "items": { @@ -346,7 +349,12 @@ func init() { "type": "boolean" }, "shareMode": { - "type": "string" + "type": "string", + "enum": [ + "public", + "private", + "reserved" + ] }, "target": { "type": "string" @@ -422,7 +430,7 @@ func init() { "description": "unauthorized" }, "500": { - "description": "internal server er" + "description": "" } } } @@ -2914,6 +2922,9 @@ func init() { "type": "string" } }, + "envZId": { + "type": "string" + }, "frontendSelection": { "type": "array", "items": { @@ -2939,7 +2950,12 @@ func init() { "type": "boolean" }, "shareMode": { - "type": "string" + "type": "string", + "enum": [ + "public", + "private", + "reserved" + ] }, "target": { "type": "string" @@ -3015,7 +3031,7 @@ func init() { "description": "unauthorized" }, "500": { - "description": "internal server er" + "description": "" } } } diff --git a/rest_server_zrok/operations/agent/remote_share.go b/rest_server_zrok/operations/agent/remote_share.go index f76a619f..f0022de5 100644 --- a/rest_server_zrok/operations/agent/remote_share.go +++ b/rest_server_zrok/operations/agent/remote_share.go @@ -91,6 +91,9 @@ type RemoteShareBody struct { // basic auth BasicAuth []string `json:"basicAuth"` + // env z Id + EnvZID string `json:"envZId,omitempty"` + // frontend selection FrontendSelection []string `json:"frontendSelection"` @@ -110,6 +113,7 @@ type RemoteShareBody struct { Open bool `json:"open,omitempty"` // share mode + // Enum: ["public","private","reserved"] ShareMode string `json:"shareMode,omitempty"` // target @@ -127,6 +131,10 @@ func (o *RemoteShareBody) Validate(formats strfmt.Registry) error { res = append(res, err) } + if err := o.validateShareMode(formats); err != nil { + res = append(res, err) + } + if len(res) > 0 { return errors.CompositeValidationError(res...) } @@ -193,6 +201,51 @@ func (o *RemoteShareBody) validateBackendMode(formats strfmt.Registry) error { return nil } +var remoteShareBodyTypeShareModePropEnum []interface{} + +func init() { + var res []string + if err := json.Unmarshal([]byte(`["public","private","reserved"]`), &res); err != nil { + panic(err) + } + for _, v := range res { + remoteShareBodyTypeShareModePropEnum = append(remoteShareBodyTypeShareModePropEnum, v) + } +} + +const ( + + // RemoteShareBodyShareModePublic captures enum value "public" + RemoteShareBodyShareModePublic string = "public" + + // RemoteShareBodyShareModePrivate captures enum value "private" + RemoteShareBodyShareModePrivate string = "private" + + // RemoteShareBodyShareModeReserved captures enum value "reserved" + RemoteShareBodyShareModeReserved string = "reserved" +) + +// prop value enum +func (o *RemoteShareBody) validateShareModeEnum(path, location string, value string) error { + if err := validate.EnumCase(path, location, value, remoteShareBodyTypeShareModePropEnum, true); err != nil { + return err + } + return nil +} + +func (o *RemoteShareBody) validateShareMode(formats strfmt.Registry) error { + if swag.IsZero(o.ShareMode) { // not required + return nil + } + + // value enum + if err := o.validateShareModeEnum("body"+"."+"shareMode", "body", o.ShareMode); err != nil { + return err + } + + return nil +} + // ContextValidate validates this remote share body based on context it is used func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { return nil diff --git a/rest_server_zrok/operations/agent/unenroll_responses.go b/rest_server_zrok/operations/agent/unenroll_responses.go index 6b9cab81..ebe96dd9 100644 --- a/rest_server_zrok/operations/agent/unenroll_responses.go +++ b/rest_server_zrok/operations/agent/unenroll_responses.go @@ -90,7 +90,7 @@ func (o *UnenrollUnauthorized) WriteResponse(rw http.ResponseWriter, producer ru const UnenrollInternalServerErrorCode int = 500 /* -UnenrollInternalServerError internal server er +UnenrollInternalServerError unenroll internal server error swagger:response unenrollInternalServerError */ diff --git a/sdk/nodejs/sdk/src/api/models/RemoteShareRequest.ts b/sdk/nodejs/sdk/src/api/models/RemoteShareRequest.ts index 0b1fd8ee..42d5212b 100644 --- a/sdk/nodejs/sdk/src/api/models/RemoteShareRequest.ts +++ b/sdk/nodejs/sdk/src/api/models/RemoteShareRequest.ts @@ -24,7 +24,13 @@ export interface RemoteShareRequest { * @type {string} * @memberof RemoteShareRequest */ - shareMode?: string; + envZId?: string; + /** + * + * @type {string} + * @memberof RemoteShareRequest + */ + shareMode?: RemoteShareRequestShareModeEnum; /** * * @type {string} @@ -94,6 +100,16 @@ export interface RemoteShareRequest { } +/** + * @export + */ +export const RemoteShareRequestShareModeEnum = { + Public: 'public', + Private: 'private', + Reserved: 'reserved' +} as const; +export type RemoteShareRequestShareModeEnum = typeof RemoteShareRequestShareModeEnum[keyof typeof RemoteShareRequestShareModeEnum]; + /** * @export */ @@ -127,6 +143,7 @@ export function RemoteShareRequestFromJSONTyped(json: any, ignoreDiscriminator: } return { + 'envZId': json['envZId'] == null ? undefined : json['envZId'], 'shareMode': json['shareMode'] == null ? undefined : json['shareMode'], 'token': json['token'] == null ? undefined : json['token'], 'target': json['target'] == null ? undefined : json['target'], @@ -153,6 +170,7 @@ export function RemoteShareRequestToJSONTyped(value?: RemoteShareRequest | null, return { + 'envZId': value['envZId'], 'shareMode': value['shareMode'], 'token': value['token'], 'target': value['target'], diff --git a/sdk/python/src/docs/AgentApi.md b/sdk/python/src/docs/AgentApi.md index 331f1816..feaf9076 100644 --- a/sdk/python/src/docs/AgentApi.md +++ b/sdk/python/src/docs/AgentApi.md @@ -391,7 +391,6 @@ void (empty response body) **200** | ok | - | **400** | bad request; not enrolled | - | **401** | unauthorized | - | -**500** | internal server er | - | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) diff --git a/sdk/python/src/docs/RemoteShareRequest.md b/sdk/python/src/docs/RemoteShareRequest.md index 2bfd86c0..901500c7 100644 --- a/sdk/python/src/docs/RemoteShareRequest.md +++ b/sdk/python/src/docs/RemoteShareRequest.md @@ -5,6 +5,7 @@ Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- +**env_zid** | **str** | | [optional] **share_mode** | **str** | | [optional] **token** | **str** | | [optional] **target** | **str** | | [optional] diff --git a/sdk/python/src/test/test_remote_share_request.py b/sdk/python/src/test/test_remote_share_request.py index 81fb4b0b..4bbaadcf 100644 --- a/sdk/python/src/test/test_remote_share_request.py +++ b/sdk/python/src/test/test_remote_share_request.py @@ -35,7 +35,8 @@ class TestRemoteShareRequest(unittest.TestCase): model = RemoteShareRequest() if include_optional: return RemoteShareRequest( - share_mode = '', + env_zid = '', + share_mode = 'public', token = '', target = '', basic_auth = [ diff --git a/sdk/python/src/zrok_api/api/agent_api.py b/sdk/python/src/zrok_api/api/agent_api.py index 5e5c0395..936906c4 100644 --- a/sdk/python/src/zrok_api/api/agent_api.py +++ b/sdk/python/src/zrok_api/api/agent_api.py @@ -1210,7 +1210,6 @@ class AgentApi: '200': None, '400': None, '401': None, - '500': None, } response_data = self.api_client.call_api( *_param, @@ -1279,7 +1278,6 @@ class AgentApi: '200': None, '400': None, '401': None, - '500': None, } response_data = self.api_client.call_api( *_param, @@ -1348,7 +1346,6 @@ class AgentApi: '200': None, '400': None, '401': None, - '500': None, } response_data = self.api_client.call_api( *_param, diff --git a/sdk/python/src/zrok_api/models/remote_share_request.py b/sdk/python/src/zrok_api/models/remote_share_request.py index 6763c89e..40d61d07 100644 --- a/sdk/python/src/zrok_api/models/remote_share_request.py +++ b/sdk/python/src/zrok_api/models/remote_share_request.py @@ -26,6 +26,7 @@ class RemoteShareRequest(BaseModel): """ RemoteShareRequest """ # noqa: E501 + env_zid: Optional[StrictStr] = Field(default=None, alias="envZId") share_mode: Optional[StrictStr] = Field(default=None, alias="shareMode") token: Optional[StrictStr] = None target: Optional[StrictStr] = None @@ -38,7 +39,17 @@ class RemoteShareRequest(BaseModel): oauth_check_interval: Optional[StrictStr] = Field(default=None, alias="oauthCheckInterval") open: Optional[StrictBool] = None access_grants: Optional[List[StrictStr]] = Field(default=None, alias="accessGrants") - __properties: ClassVar[List[str]] = ["shareMode", "token", "target", "basicAuth", "frontendSelection", "backendMode", "insecure", "oauthProvider", "oauthEmailAddressPatterns", "oauthCheckInterval", "open", "accessGrants"] + __properties: ClassVar[List[str]] = ["envZId", "shareMode", "token", "target", "basicAuth", "frontendSelection", "backendMode", "insecure", "oauthProvider", "oauthEmailAddressPatterns", "oauthCheckInterval", "open", "accessGrants"] + + @field_validator('share_mode') + def share_mode_validate_enum(cls, value): + """Validates the enum""" + if value is None: + return value + + if value not in set(['public', 'private', 'reserved']): + raise ValueError("must be one of enum values ('public', 'private', 'reserved')") + return value @field_validator('backend_mode') def backend_mode_validate_enum(cls, value): @@ -101,6 +112,7 @@ class RemoteShareRequest(BaseModel): return cls.model_validate(obj) _obj = cls.model_validate({ + "envZId": obj.get("envZId"), "shareMode": obj.get("shareMode"), "token": obj.get("token"), "target": obj.get("target"), diff --git a/specs/zrok.yml b/specs/zrok.yml index 62d0e7f1..2599999d 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -644,30 +644,6 @@ paths: 500: description: internal server error - /agent/unenroll: - post: - tags: - - agent - security: - - key: [] - operationId: unenroll - parameters: - - name: body - in: body - schema: - properties: - envZId: - type: string - responses: - 200: - description: ok - 400: - description: bad request; not enrolled - 401: - description: unauthorized - 500: - description: internal server er - /agent/ping: post: tags: @@ -708,8 +684,11 @@ paths: in: body schema: properties: + envZId: + type: string shareMode: type: string + enum: ["public", "private", "reserved"] token: type: string target: @@ -759,6 +738,29 @@ paths: 502: description: bad gateway; agent not reachable + /agent/unenroll: + post: + tags: + - agent + security: + - key: [] + operationId: unenroll + parameters: + - name: body + in: body + schema: + properties: + envZId: + type: string + responses: + 200: + description: ok + 400: + description: bad request; not enrolled + 401: + description: unauthorized + 500: + /agent/unshare: post: tags: diff --git a/ui/src/api/models/RemoteShareRequest.ts b/ui/src/api/models/RemoteShareRequest.ts index 0b1fd8ee..42d5212b 100644 --- a/ui/src/api/models/RemoteShareRequest.ts +++ b/ui/src/api/models/RemoteShareRequest.ts @@ -24,7 +24,13 @@ export interface RemoteShareRequest { * @type {string} * @memberof RemoteShareRequest */ - shareMode?: string; + envZId?: string; + /** + * + * @type {string} + * @memberof RemoteShareRequest + */ + shareMode?: RemoteShareRequestShareModeEnum; /** * * @type {string} @@ -94,6 +100,16 @@ export interface RemoteShareRequest { } +/** + * @export + */ +export const RemoteShareRequestShareModeEnum = { + Public: 'public', + Private: 'private', + Reserved: 'reserved' +} as const; +export type RemoteShareRequestShareModeEnum = typeof RemoteShareRequestShareModeEnum[keyof typeof RemoteShareRequestShareModeEnum]; + /** * @export */ @@ -127,6 +143,7 @@ export function RemoteShareRequestFromJSONTyped(json: any, ignoreDiscriminator: } return { + 'envZId': json['envZId'] == null ? undefined : json['envZId'], 'shareMode': json['shareMode'] == null ? undefined : json['shareMode'], 'token': json['token'] == null ? undefined : json['token'], 'target': json['target'] == null ? undefined : json['target'], @@ -153,6 +170,7 @@ export function RemoteShareRequestToJSONTyped(value?: RemoteShareRequest | null, return { + 'envZId': value['envZId'], 'shareMode': value['shareMode'], 'token': value['token'], 'target': value['target'],