implementation of /agent/share (#967)

This commit is contained in:
Michael Quigley 2025-06-02 15:56:48 -04:00
parent e991ecf0cb
commit 1aad5ff2b2
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
17 changed files with 338 additions and 42 deletions

View File

@ -47,8 +47,8 @@ func (a *Agent) SharePrivate(req *SharePrivateRequest) (shareToken string, err e
} }
shr.insecure = req.Insecure shr.insecure = req.Insecure
if req.Closed { if !req.Closed {
shrCmd = append(shrCmd, "--closed") shrCmd = append(shrCmd, "--open")
} }
shr.closed = req.Closed shr.closed = req.Closed

View File

@ -71,8 +71,8 @@ func (a *Agent) SharePublic(req *SharePublicRequest) (shareToken string, fronten
shrCmd = append(shrCmd, "--oauth-check-interval", req.OauthCheckInterval) shrCmd = append(shrCmd, "--oauth-check-interval", req.OauthCheckInterval)
} }
if req.Closed { if !req.Closed {
shrCmd = append(shrCmd, "--closed") shrCmd = append(shrCmd, "--open")
} }
shr.closed = req.Closed shr.closed = req.Closed

View File

@ -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
}

View File

@ -68,6 +68,7 @@ func Run(inCfg *config.Config) error {
if cfg.AgentController != nil { if cfg.AgentController != nil {
api.AgentEnrollHandler = newAgentEnrollHandler() api.AgentEnrollHandler = newAgentEnrollHandler()
api.AgentPingHandler = newAgentPingHandler() api.AgentPingHandler = newAgentPingHandler()
api.AgentRemoteShareHandler = newAgentRemoteShareHandler()
api.AgentUnenrollHandler = newAgentUnenrollHandler() api.AgentUnenrollHandler = newAgentUnenrollHandler()
} }
api.EnvironmentEnableHandler = newEnableHandler() api.EnvironmentEnableHandler = newEnableHandler()

View File

@ -309,6 +309,9 @@ type RemoteShareBody struct {
// basic auth // basic auth
BasicAuth []string `json:"basicAuth"` BasicAuth []string `json:"basicAuth"`
// env z Id
EnvZID string `json:"envZId,omitempty"`
// frontend selection // frontend selection
FrontendSelection []string `json:"frontendSelection"` FrontendSelection []string `json:"frontendSelection"`
@ -328,6 +331,7 @@ type RemoteShareBody struct {
Open bool `json:"open,omitempty"` Open bool `json:"open,omitempty"`
// share mode // share mode
// Enum: ["public","private","reserved"]
ShareMode string `json:"shareMode,omitempty"` ShareMode string `json:"shareMode,omitempty"`
// target // target
@ -345,6 +349,10 @@ func (o *RemoteShareBody) Validate(formats strfmt.Registry) error {
res = append(res, err) res = append(res, err)
} }
if err := o.validateShareMode(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 { if len(res) > 0 {
return errors.CompositeValidationError(res...) return errors.CompositeValidationError(res...)
} }
@ -411,6 +419,51 @@ func (o *RemoteShareBody) validateBackendMode(formats strfmt.Registry) error {
return nil 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 // ContextValidate validates this remote share body based on context it is used
func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil return nil

View File

@ -227,7 +227,7 @@ func NewUnenrollInternalServerError() *UnenrollInternalServerError {
/* /*
UnenrollInternalServerError describes a response with status code 500, with default header values. UnenrollInternalServerError describes a response with status code 500, with default header values.
internal server er UnenrollInternalServerError unenroll internal server error
*/ */
type UnenrollInternalServerError struct { type UnenrollInternalServerError struct {
} }

View File

@ -321,6 +321,9 @@ func init() {
"type": "string" "type": "string"
} }
}, },
"envZId": {
"type": "string"
},
"frontendSelection": { "frontendSelection": {
"type": "array", "type": "array",
"items": { "items": {
@ -346,7 +349,12 @@ func init() {
"type": "boolean" "type": "boolean"
}, },
"shareMode": { "shareMode": {
"type": "string" "type": "string",
"enum": [
"public",
"private",
"reserved"
]
}, },
"target": { "target": {
"type": "string" "type": "string"
@ -422,7 +430,7 @@ func init() {
"description": "unauthorized" "description": "unauthorized"
}, },
"500": { "500": {
"description": "internal server er" "description": ""
} }
} }
} }
@ -2914,6 +2922,9 @@ func init() {
"type": "string" "type": "string"
} }
}, },
"envZId": {
"type": "string"
},
"frontendSelection": { "frontendSelection": {
"type": "array", "type": "array",
"items": { "items": {
@ -2939,7 +2950,12 @@ func init() {
"type": "boolean" "type": "boolean"
}, },
"shareMode": { "shareMode": {
"type": "string" "type": "string",
"enum": [
"public",
"private",
"reserved"
]
}, },
"target": { "target": {
"type": "string" "type": "string"
@ -3015,7 +3031,7 @@ func init() {
"description": "unauthorized" "description": "unauthorized"
}, },
"500": { "500": {
"description": "internal server er" "description": ""
} }
} }
} }

View File

@ -91,6 +91,9 @@ type RemoteShareBody struct {
// basic auth // basic auth
BasicAuth []string `json:"basicAuth"` BasicAuth []string `json:"basicAuth"`
// env z Id
EnvZID string `json:"envZId,omitempty"`
// frontend selection // frontend selection
FrontendSelection []string `json:"frontendSelection"` FrontendSelection []string `json:"frontendSelection"`
@ -110,6 +113,7 @@ type RemoteShareBody struct {
Open bool `json:"open,omitempty"` Open bool `json:"open,omitempty"`
// share mode // share mode
// Enum: ["public","private","reserved"]
ShareMode string `json:"shareMode,omitempty"` ShareMode string `json:"shareMode,omitempty"`
// target // target
@ -127,6 +131,10 @@ func (o *RemoteShareBody) Validate(formats strfmt.Registry) error {
res = append(res, err) res = append(res, err)
} }
if err := o.validateShareMode(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 { if len(res) > 0 {
return errors.CompositeValidationError(res...) return errors.CompositeValidationError(res...)
} }
@ -193,6 +201,51 @@ func (o *RemoteShareBody) validateBackendMode(formats strfmt.Registry) error {
return nil 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 // ContextValidate validates this remote share body based on context it is used
func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error { func (o *RemoteShareBody) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil return nil

View File

@ -90,7 +90,7 @@ func (o *UnenrollUnauthorized) WriteResponse(rw http.ResponseWriter, producer ru
const UnenrollInternalServerErrorCode int = 500 const UnenrollInternalServerErrorCode int = 500
/* /*
UnenrollInternalServerError internal server er UnenrollInternalServerError unenroll internal server error
swagger:response unenrollInternalServerError swagger:response unenrollInternalServerError
*/ */

View File

@ -24,7 +24,13 @@ export interface RemoteShareRequest {
* @type {string} * @type {string}
* @memberof RemoteShareRequest * @memberof RemoteShareRequest
*/ */
shareMode?: string; envZId?: string;
/**
*
* @type {string}
* @memberof RemoteShareRequest
*/
shareMode?: RemoteShareRequestShareModeEnum;
/** /**
* *
* @type {string} * @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 * @export
*/ */
@ -127,6 +143,7 @@ export function RemoteShareRequestFromJSONTyped(json: any, ignoreDiscriminator:
} }
return { return {
'envZId': json['envZId'] == null ? undefined : json['envZId'],
'shareMode': json['shareMode'] == null ? undefined : json['shareMode'], 'shareMode': json['shareMode'] == null ? undefined : json['shareMode'],
'token': json['token'] == null ? undefined : json['token'], 'token': json['token'] == null ? undefined : json['token'],
'target': json['target'] == null ? undefined : json['target'], 'target': json['target'] == null ? undefined : json['target'],
@ -153,6 +170,7 @@ export function RemoteShareRequestToJSONTyped(value?: RemoteShareRequest | null,
return { return {
'envZId': value['envZId'],
'shareMode': value['shareMode'], 'shareMode': value['shareMode'],
'token': value['token'], 'token': value['token'],
'target': value['target'], 'target': value['target'],

View File

@ -391,7 +391,6 @@ void (empty response body)
**200** | ok | - | **200** | ok | - |
**400** | bad request; not enrolled | - | **400** | bad request; not enrolled | - |
**401** | unauthorized | - | **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) [[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)

View File

@ -5,6 +5,7 @@
Name | Type | Description | Notes Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**env_zid** | **str** | | [optional]
**share_mode** | **str** | | [optional] **share_mode** | **str** | | [optional]
**token** | **str** | | [optional] **token** | **str** | | [optional]
**target** | **str** | | [optional] **target** | **str** | | [optional]

View File

@ -35,7 +35,8 @@ class TestRemoteShareRequest(unittest.TestCase):
model = RemoteShareRequest() model = RemoteShareRequest()
if include_optional: if include_optional:
return RemoteShareRequest( return RemoteShareRequest(
share_mode = '', env_zid = '',
share_mode = 'public',
token = '', token = '',
target = '', target = '',
basic_auth = [ basic_auth = [

View File

@ -1210,7 +1210,6 @@ class AgentApi:
'200': None, '200': None,
'400': None, '400': None,
'401': None, '401': None,
'500': None,
} }
response_data = self.api_client.call_api( response_data = self.api_client.call_api(
*_param, *_param,
@ -1279,7 +1278,6 @@ class AgentApi:
'200': None, '200': None,
'400': None, '400': None,
'401': None, '401': None,
'500': None,
} }
response_data = self.api_client.call_api( response_data = self.api_client.call_api(
*_param, *_param,
@ -1348,7 +1346,6 @@ class AgentApi:
'200': None, '200': None,
'400': None, '400': None,
'401': None, '401': None,
'500': None,
} }
response_data = self.api_client.call_api( response_data = self.api_client.call_api(
*_param, *_param,

View File

@ -26,6 +26,7 @@ class RemoteShareRequest(BaseModel):
""" """
RemoteShareRequest RemoteShareRequest
""" # noqa: E501 """ # noqa: E501
env_zid: Optional[StrictStr] = Field(default=None, alias="envZId")
share_mode: Optional[StrictStr] = Field(default=None, alias="shareMode") share_mode: Optional[StrictStr] = Field(default=None, alias="shareMode")
token: Optional[StrictStr] = None token: Optional[StrictStr] = None
target: Optional[StrictStr] = None target: Optional[StrictStr] = None
@ -38,7 +39,17 @@ class RemoteShareRequest(BaseModel):
oauth_check_interval: Optional[StrictStr] = Field(default=None, alias="oauthCheckInterval") oauth_check_interval: Optional[StrictStr] = Field(default=None, alias="oauthCheckInterval")
open: Optional[StrictBool] = None open: Optional[StrictBool] = None
access_grants: Optional[List[StrictStr]] = Field(default=None, alias="accessGrants") 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') @field_validator('backend_mode')
def backend_mode_validate_enum(cls, value): def backend_mode_validate_enum(cls, value):
@ -101,6 +112,7 @@ class RemoteShareRequest(BaseModel):
return cls.model_validate(obj) return cls.model_validate(obj)
_obj = cls.model_validate({ _obj = cls.model_validate({
"envZId": obj.get("envZId"),
"shareMode": obj.get("shareMode"), "shareMode": obj.get("shareMode"),
"token": obj.get("token"), "token": obj.get("token"),
"target": obj.get("target"), "target": obj.get("target"),

View File

@ -644,30 +644,6 @@ paths:
500: 500:
description: internal server error 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: /agent/ping:
post: post:
tags: tags:
@ -708,8 +684,11 @@ paths:
in: body in: body
schema: schema:
properties: properties:
envZId:
type: string
shareMode: shareMode:
type: string type: string
enum: ["public", "private", "reserved"]
token: token:
type: string type: string
target: target:
@ -759,6 +738,29 @@ paths:
502: 502:
description: bad gateway; agent not reachable 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: /agent/unshare:
post: post:
tags: tags:

View File

@ -24,7 +24,13 @@ export interface RemoteShareRequest {
* @type {string} * @type {string}
* @memberof RemoteShareRequest * @memberof RemoteShareRequest
*/ */
shareMode?: string; envZId?: string;
/**
*
* @type {string}
* @memberof RemoteShareRequest
*/
shareMode?: RemoteShareRequestShareModeEnum;
/** /**
* *
* @type {string} * @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 * @export
*/ */
@ -127,6 +143,7 @@ export function RemoteShareRequestFromJSONTyped(json: any, ignoreDiscriminator:
} }
return { return {
'envZId': json['envZId'] == null ? undefined : json['envZId'],
'shareMode': json['shareMode'] == null ? undefined : json['shareMode'], 'shareMode': json['shareMode'] == null ? undefined : json['shareMode'],
'token': json['token'] == null ? undefined : json['token'], 'token': json['token'] == null ? undefined : json['token'],
'target': json['target'] == null ? undefined : json['target'], 'target': json['target'] == null ? undefined : json['target'],
@ -153,6 +170,7 @@ export function RemoteShareRequestToJSONTyped(value?: RemoteShareRequest | null,
return { return {
'envZId': value['envZId'],
'shareMode': value['shareMode'], 'shareMode': value['shareMode'],
'token': value['token'], 'token': value['token'],
'target': value['target'], 'target': value['target'],