diff --git a/CHANGELOG.md b/CHANGELOG.md index be1bffcb..f03c4e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## v0.4.25 + +CHANGE: Creating a reserved share checks for token collision and returns a more appropriate error message (https://github.com/openziti/zrok/issues/531) + +CHANGE: Update UI to add a 'true' value on `reserved` boolean (https://github.com/openziti/zrok/issues/443) + ## v0.4.24 FEATURE: New `socks` backend mode for use with private sharing. Use `zrok share private --backend-mode socks` and then `zrok access private` that share from somewhere else... very lightweight VPN-like functionality (https://github.com/openziti/zrok/issues/558) diff --git a/controller/share.go b/controller/share.go index dbb22c2e..e2fbbcf5 100644 --- a/controller/share.go +++ b/controller/share.go @@ -135,6 +135,16 @@ func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zr sshr.FrontendEndpoint = &sshr.ShareMode } + sh, err := str.FindShareWithToken(sshr.Token, trx) + if err != nil { + logrus.Errorf("error checking share for token collision: %v", err) + return share.NewShareInternalServerError() + } + if sh != nil { + logrus.Errorf("token '%v' already exists; cannot create share", sshr.Token) + return share.NewShareConflict() + } + sid, err := str.CreateShare(envId, sshr, trx) if err != nil { logrus.Errorf("error creating share record: %v", err) diff --git a/rest_client_zrok/share/share_responses.go b/rest_client_zrok/share/share_responses.go index 346cbdf3..8af0e374 100644 --- a/rest_client_zrok/share/share_responses.go +++ b/rest_client_zrok/share/share_responses.go @@ -41,6 +41,12 @@ func (o *ShareReader) ReadResponse(response runtime.ClientResponse, consumer run return nil, err } return nil, result + case 409: + result := NewShareConflict() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result case 422: result := NewShareUnprocessableEntity() if err := result.readResponse(response, consumer, o.formats); err != nil { @@ -238,6 +244,62 @@ func (o *ShareNotFound) readResponse(response runtime.ClientResponse, consumer r return nil } +// NewShareConflict creates a ShareConflict with default headers values +func NewShareConflict() *ShareConflict { + return &ShareConflict{} +} + +/* +ShareConflict describes a response with status code 409, with default header values. + +conflict +*/ +type ShareConflict struct { +} + +// IsSuccess returns true when this share conflict response has a 2xx status code +func (o *ShareConflict) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this share conflict response has a 3xx status code +func (o *ShareConflict) IsRedirect() bool { + return false +} + +// IsClientError returns true when this share conflict response has a 4xx status code +func (o *ShareConflict) IsClientError() bool { + return true +} + +// IsServerError returns true when this share conflict response has a 5xx status code +func (o *ShareConflict) IsServerError() bool { + return false +} + +// IsCode returns true when this share conflict response a status code equal to that given +func (o *ShareConflict) IsCode(code int) bool { + return code == 409 +} + +// Code gets the status code for the share conflict response +func (o *ShareConflict) Code() int { + return 409 +} + +func (o *ShareConflict) Error() string { + return fmt.Sprintf("[POST /share][%d] shareConflict ", 409) +} + +func (o *ShareConflict) String() string { + return fmt.Sprintf("[POST /share][%d] shareConflict ", 409) +} + +func (o *ShareConflict) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + // NewShareUnprocessableEntity creates a ShareUnprocessableEntity with default headers values func NewShareUnprocessableEntity() *ShareUnprocessableEntity { return &ShareUnprocessableEntity{} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index c2a6856d..f04c18d0 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -909,6 +909,9 @@ func init() { "404": { "description": "not found" }, + "409": { + "description": "conflict" + }, "422": { "description": "unprocessable" }, @@ -2577,6 +2580,9 @@ func init() { "404": { "description": "not found" }, + "409": { + "description": "conflict" + }, "422": { "description": "unprocessable" }, diff --git a/rest_server_zrok/operations/share/share_responses.go b/rest_server_zrok/operations/share/share_responses.go index d49c044e..9bf46588 100644 --- a/rest_server_zrok/operations/share/share_responses.go +++ b/rest_server_zrok/operations/share/share_responses.go @@ -108,6 +108,31 @@ func (o *ShareNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.P rw.WriteHeader(404) } +// ShareConflictCode is the HTTP code returned for type ShareConflict +const ShareConflictCode int = 409 + +/* +ShareConflict conflict + +swagger:response shareConflict +*/ +type ShareConflict struct { +} + +// NewShareConflict creates ShareConflict with default headers values +func NewShareConflict() *ShareConflict { + + return &ShareConflict{} +} + +// WriteResponse to the client +func (o *ShareConflict) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(409) +} + // ShareUnprocessableEntityCode is the HTTP code returned for type ShareUnprocessableEntity const ShareUnprocessableEntityCode int = 422 diff --git a/specs/zrok.yml b/specs/zrok.yml index 96afb2e0..29a596a0 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -603,6 +603,8 @@ paths: description: unauthorized 404: description: not found + 409: + description: conflict 422: description: unprocessable 500: diff --git a/ui/src/console/PropertyTable.js b/ui/src/console/PropertyTable.js index 355aec2d..14ccf711 100644 --- a/ui/src/console/PropertyTable.js +++ b/ui/src/console/PropertyTable.js @@ -18,7 +18,7 @@ const rowToValue = (row) => { if(row.property.endsWith("At")) { return new Date(row.value).toLocaleString(); } - return row.value; + return row.value.toString(); }; const PropertyTable = (props) => {