mirror of
https://github.com/openziti/ziti.git
synced 2026-10-22 19:56:07 +00:00
...and also the 'edge' bits.
This commit is contained in:
@@ -45,14 +45,14 @@ running with Edge functionality enabled.
|
||||
If you wish to start the controller with the Ziti Fabric and Ziti Edge enabled:
|
||||
|
||||
```
|
||||
ziti-controller run $GOPATH/src/bitbucket.org/netfoundry/ziti/fabric/etc/ctrl.with.edge.yml
|
||||
ziti-controller run $GOPATH/src/github.com/netfoundry/ziti-fabric/fabric/etc/ctrl.with.edge.yml
|
||||
```
|
||||
|
||||
|
||||
If you wish to start the Ziti Fabric standalone:
|
||||
|
||||
```
|
||||
ziti-controller run $GOPATH/src/bitbucket.org/netfoundry/ziti/fabric/etc/ctrl.yml
|
||||
ziti-controller run $GOPATH/src/github.com/netfoundry/ziti-fabric/fabric/etc/ctrl.yml
|
||||
```
|
||||
|
||||
Please note that if you start the controller without the Ziti Edge enabled, the Ziti SDK, and edge router functionality
|
||||
@@ -80,7 +80,7 @@ Where `XXX` is replaced with `001` through `004`.
|
||||
Run:
|
||||
|
||||
```
|
||||
ziti-router run $GOPATH/src/bitbucket.org/netfoundry/ziti/fabric/etc/XXX.yml
|
||||
ziti-router run $GOPATH/src/github.com/netfoundry/ziti-fabric/fabric/etc/XXX.yml
|
||||
```
|
||||
|
||||
Where `XXX` is replaced with `001` through `004`.
|
||||
@@ -172,7 +172,7 @@ edge routers via `GET /edge-routers`. The response from retrieving a edge router
|
||||
### Enroll the Edge Router
|
||||
|
||||
```
|
||||
ziti-router enroll --jwt <path to enrollment.jwt> $GOPATH/src/bitbucket.org/netfoundry/ziti/fabric/etc/edge.router.yml
|
||||
ziti-router enroll --jwt <path to enrollment.jwt> $GOPATH/src/github.com/netfoundry/ziti-fabric/fabric/etc/edge.router.yml
|
||||
```
|
||||
|
||||
...where `path to enrollment.jwt` is the enrollment JWT for the edge router.
|
||||
@@ -180,7 +180,7 @@ ziti-router enroll --jwt <path to enrollment.jwt> $GOPATH/src/bitbucket.org/netf
|
||||
### Start Ziti Edge Router
|
||||
|
||||
```
|
||||
ziti-router run $GOPATH/src/bitbucket.org/netfoundry/ziti/fabric/etc/edge.router.yml
|
||||
ziti-router run $GOPATH/src/github.com/netfoundry/ziti-fabric/fabric/etc/edge.router.yml
|
||||
```
|
||||
|
||||
# Further Exploration
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
cache/*.jwt
|
||||
cache/*.pem
|
||||
cache/*.key
|
||||
cache/*.cert
|
||||
cache/*.jwt
|
||||
cache/*.json
|
||||
|
||||
ignore.*
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type ApiError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Status int `json:"-"`
|
||||
Cause error `json:"cause"`
|
||||
AppendCause bool `json:"-"`
|
||||
}
|
||||
|
||||
func (e ApiError) Error() string {
|
||||
s := e.Code + ": " + e.Message
|
||||
|
||||
if e.Cause != nil && e.AppendCause {
|
||||
s = s + ": " + e.Cause.Error()
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type GenericCauseError struct {
|
||||
Message string
|
||||
DataMap map[string]interface{}
|
||||
}
|
||||
|
||||
func (e GenericCauseError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *GenericCauseError) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.DataMap)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
maxFieldErrorValueLength = 64
|
||||
)
|
||||
|
||||
|
||||
type BodyParseTypeError struct {
|
||||
Near string `json:"near"`
|
||||
ExpectedType string `json:"expectedType"`
|
||||
EncounteredType string `json:"encounteredType"`
|
||||
Offset int64 `json:"totalOffset"`
|
||||
Line int64 `json:"line"`
|
||||
}
|
||||
|
||||
func (e BodyParseTypeError) Error() string {
|
||||
return fmt.Sprintf("Expected type %s, but encountered %s near %s", e.ExpectedType, e.ExpectedType, e.Near)
|
||||
}
|
||||
|
||||
func NewBodyParseTypeError(e *json.UnmarshalTypeError, body string) *BodyParseTypeError {
|
||||
nearStart := e.Offset - 10
|
||||
nearEnd := e.Offset
|
||||
|
||||
if nearStart < 0 {
|
||||
nearEnd = 5
|
||||
}
|
||||
|
||||
near := body[nearStart:nearEnd]
|
||||
|
||||
bodyToError := body[:e.Offset]
|
||||
|
||||
line := strings.Count(bodyToError, "\n") + 1
|
||||
|
||||
return &BodyParseTypeError{
|
||||
ExpectedType: e.Type.String(),
|
||||
EncounteredType: e.Value,
|
||||
Near: near,
|
||||
Offset: e.Offset,
|
||||
Line: int64(line),
|
||||
}
|
||||
}
|
||||
|
||||
type BodyParseSyntaxError struct {
|
||||
Near string `json:"near"`
|
||||
Offset int64 `json:"totalOffset"`
|
||||
Line int64 `json:"line"`
|
||||
}
|
||||
|
||||
func (e BodyParseSyntaxError) Error() string {
|
||||
return fmt.Sprintf("Invalid syntax on line %d", e.Line)
|
||||
}
|
||||
|
||||
func NewBodyParseSyntaxError(e *json.SyntaxError, body string) *BodyParseSyntaxError {
|
||||
nearStart := e.Offset - 10
|
||||
nearEnd := e.Offset
|
||||
|
||||
if nearStart < 0 {
|
||||
nearStart = 0
|
||||
nearEnd = 5
|
||||
}
|
||||
|
||||
if nearEnd > int64(len(body)) {
|
||||
nearEnd = int64(len(body))
|
||||
}
|
||||
|
||||
near := body[nearStart:nearEnd]
|
||||
|
||||
bodyToError := body[:e.Offset]
|
||||
|
||||
line := strings.Count(bodyToError, "\n") + 1
|
||||
|
||||
return &BodyParseSyntaxError{
|
||||
Near: near,
|
||||
Offset: e.Offset,
|
||||
Line: int64(line),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import "fmt"
|
||||
|
||||
type FieldError struct {
|
||||
Reason string `json:"message"`
|
||||
FieldName string `json:"field"`
|
||||
FieldValue interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func (fe FieldError) Error() string {
|
||||
v := fe.FieldValue
|
||||
if s, ok := fe.FieldValue.(string); ok {
|
||||
if len(s) > maxFieldErrorValueLength {
|
||||
v = s[0:maxFieldErrorValueLength] + "..."
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("the value '%v' for '%s' is invalid: %s", v, fe.FieldName, fe.Reason)
|
||||
}
|
||||
|
||||
func NewFieldError(reason, name string, value interface{}) *FieldError {
|
||||
return &FieldError{
|
||||
Reason: reason,
|
||||
FieldName: name,
|
||||
FieldValue: value,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import (
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewNotFound() *ApiError {
|
||||
return &ApiError{
|
||||
Code: NotFoundCode,
|
||||
Message: NotFoundMessage,
|
||||
Status: NotFoundStatus,
|
||||
}
|
||||
}
|
||||
func NewForbidden() *ApiError {
|
||||
return &ApiError{
|
||||
Code: ForbiddenCode,
|
||||
Message: ForbiddenMessage,
|
||||
Status: ForbiddenStatus,
|
||||
}
|
||||
}
|
||||
func NewUnhandled() *ApiError {
|
||||
return &ApiError{
|
||||
Code: UnhandledCode,
|
||||
Message: UnhandledMessage,
|
||||
Status: UnhandledStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotParseBody() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotParseBodyCode,
|
||||
Message: CouldNotParseBodyMessage,
|
||||
Status: CouldNotParseBodyStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func NewInvalidContentType(contentType string) *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidContentTypeCode,
|
||||
Message: InvalidContentTypeMessage + ": " + contentType,
|
||||
Status: InvalidContentTypeStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCouldNotReadBody() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotReadBodyCode,
|
||||
Message: CouldNotReadBodyMessage,
|
||||
Status: CouldNotReadBodyStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidField() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidFieldCode,
|
||||
Message: InvalidFieldMessage,
|
||||
Status: InvalidFieldStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEntityCanNotBeDeleted() *ApiError {
|
||||
return &ApiError{
|
||||
Code: EntityCanNotBeDeletedCode,
|
||||
Message: EntityCanNotBeDeletedMessage,
|
||||
Status: EntityCanNotBeDeletedStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidAuth() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidAuthCode,
|
||||
Message: InvalidAuthMessage,
|
||||
Status: InvalidAuthStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidAuthMethod() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidAuthMethodCode,
|
||||
Message: InvalidAuthMethodMessage,
|
||||
Status: InvalidAuthMethodStatus,
|
||||
}
|
||||
}
|
||||
func NewEnrollmentExpired() *ApiError {
|
||||
return &ApiError{
|
||||
Code: EnrollmentExpiredCode,
|
||||
Message: EnrollmentExpiredMessage,
|
||||
Status: EnrollmentExpiredStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotProcessCsr() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotProcessCsrCode,
|
||||
Message: CouldNotProcessCsrMessage,
|
||||
Status: CouldNotProcessCsrStatus,
|
||||
}
|
||||
}
|
||||
func NewEnrollmentCaNoLongValid() *ApiError {
|
||||
return &ApiError{
|
||||
Code: EnrollmentCaNoLongValidCode,
|
||||
Message: EnrollmentCaNoLongValidMessage,
|
||||
Status: EnrollmentCaNoLongValidStatus,
|
||||
}
|
||||
}
|
||||
func NewEnrollmentNoValidCas() *ApiError {
|
||||
return &ApiError{
|
||||
Code: EnrollmentNoValidCasCode,
|
||||
Message: EnrollmentNoValidCasMessage,
|
||||
Status: EnrollmentNoValidCasStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidEnrollmentToken() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidEnrollmentTokenCode,
|
||||
Message: InvalidEnrollmentTokenMessage,
|
||||
Status: InvalidEnrollmentTokenStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidEnrollMethod() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidEnrollMethodCode,
|
||||
Message: InvalidEnrollMethodMessage,
|
||||
Status: InvalidEnrollMethodStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidFilter() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidFilterCode,
|
||||
Message: InvalidFilterMessage,
|
||||
Status: InvalidFilterStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidPagination() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidPaginationCode,
|
||||
Message: InvalidPaginationMessage,
|
||||
Status: InvalidPaginationStatus,
|
||||
}
|
||||
}
|
||||
func NewNoEdgeRoutersAvailable() *ApiError {
|
||||
return &ApiError{
|
||||
Code: NoEdgeRoutersAvailableCode,
|
||||
Message: NoEdgeRoutersAvailableMessage,
|
||||
Status: NoEdgeRoutersAvailableStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidSort() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidSortCode,
|
||||
Message: InvalidSortMessage,
|
||||
Status: InvalidSortStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotValidate() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotValidateCode,
|
||||
Message: CouldNotValidateMessage,
|
||||
Status: CouldNotValidateStatus,
|
||||
}
|
||||
}
|
||||
func NewUnauthorized() *ApiError {
|
||||
return &ApiError{
|
||||
Code: UnauthorizedCode,
|
||||
Message: UnauthorizedMessage,
|
||||
Status: UnauthorizedStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotParseX509FromDer() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotParseX509FromDerCode,
|
||||
Message: CouldNotParseX509FromDerMessage,
|
||||
Status: CouldNotParseX509FromDerStatus,
|
||||
}
|
||||
}
|
||||
func NewCertFailedValidation() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CertFailedValidationCode,
|
||||
Message: CertFailedValidationMessage,
|
||||
Status: CertFailedValidationStatus,
|
||||
}
|
||||
}
|
||||
func NewCertInUse() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CertInUseCode,
|
||||
Message: CertInUseMessage,
|
||||
Status: CertInUseStatus,
|
||||
}
|
||||
}
|
||||
func NewCaAlreadyVerified() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CaAlreadyVerifiedCode,
|
||||
Message: CaAlreadyVerifiedMessage,
|
||||
Status: CaAlreadyVerifiedStatus,
|
||||
}
|
||||
}
|
||||
func NewExpectedPemBlockCertificate() *ApiError {
|
||||
return &ApiError{
|
||||
Code: ExpectedPemBlockCertificateCode,
|
||||
Message: ExpectedPemBlockCertificateMessage,
|
||||
Status: ExpectedPemBlockCertificateStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotParseDerBlock() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotParseDerBlockCode,
|
||||
Message: CouldNotParseDerBlockMessage,
|
||||
Status: CouldNotParseDerBlockStatus,
|
||||
}
|
||||
}
|
||||
func NewCouldNotParsePem() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CouldNotParsePemCode,
|
||||
Message: CouldNotParsePemMessage,
|
||||
Status: CouldNotParsePemStatus,
|
||||
}
|
||||
}
|
||||
func NewInvalidCommonName() *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidCommonNameCode,
|
||||
Message: InvalidCommonNameMessage,
|
||||
Status: InvalidCommonNameStatus,
|
||||
}
|
||||
}
|
||||
func NewFailedCertificateValidation() *ApiError {
|
||||
return &ApiError{
|
||||
Code: FailedCertificateValidationCode,
|
||||
Message: FailedCertificateValidationMessage,
|
||||
Status: FailedCertificateValidationStatus,
|
||||
}
|
||||
}
|
||||
func NewCertificateIsNotCa() *ApiError {
|
||||
return &ApiError{
|
||||
Code: CertificateIsNotCaCode,
|
||||
Message: CertificateIsNotCaMessage,
|
||||
Status: CertificateIsNotCaStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func NewValidationError(err gojsonschema.ResultError) *ValidationError {
|
||||
return &ValidationError{
|
||||
Field: err.Field(),
|
||||
Type: err.Type(),
|
||||
Value: err.Value(),
|
||||
Message: err.String(),
|
||||
Details: err.Details(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewField(fieldError *FieldError) *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidFieldCode,
|
||||
Message: InvalidFieldMessage,
|
||||
Status: http.StatusBadRequest,
|
||||
Cause: fieldError,
|
||||
AppendCause: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewInvalidUuid(val string) *ApiError {
|
||||
return &ApiError{
|
||||
Code: InvalidUuidCode,
|
||||
Message: InvalidUuidMessage,
|
||||
Status: InvalidUuidStatus,
|
||||
Cause: &GenericCauseError{
|
||||
Message: "invalid uuid",
|
||||
DataMap: map[string]interface{}{
|
||||
"uuid": val,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func GetJsonParseError(e error, body []byte) error {
|
||||
if ute, ok := e.(*json.UnmarshalTypeError); ok {
|
||||
e = NewBodyParseTypeError(ute, string(body))
|
||||
} else if se, ok := e.(*json.SyntaxError); ok {
|
||||
e = NewBodyParseSyntaxError(se, string(body))
|
||||
}
|
||||
return e
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import "net/http"
|
||||
|
||||
const (
|
||||
NotFoundCode string = "NOT_FOUND"
|
||||
NotFoundMessage string = "The resource requested was not found or is no longer available"
|
||||
NotFoundStatus int = http.StatusNotFound
|
||||
|
||||
ForbiddenCode string = "FORBIDDEN"
|
||||
ForbiddenMessage string = "The resource requested"
|
||||
ForbiddenStatus int = http.StatusForbidden
|
||||
|
||||
UnhandledCode string = "UNHANDLED"
|
||||
UnhandledMessage string = "An unhandled error occurred"
|
||||
UnhandledStatus int = http.StatusInternalServerError
|
||||
|
||||
CouldNotParseBodyCode string = "COULD_NOT_PARSE_BODY"
|
||||
CouldNotParseBodyMessage string = "The body of the request could not be parsed"
|
||||
CouldNotParseBodyStatus int = http.StatusInternalServerError
|
||||
|
||||
CouldNotReadBodyCode string = "COULD_NOT_READ_BODY"
|
||||
CouldNotReadBodyMessage string = "The body of the request could not be read"
|
||||
CouldNotReadBodyStatus int = http.StatusInternalServerError
|
||||
|
||||
InvalidFieldCode string = "INVALID_FIELD"
|
||||
InvalidFieldMessage string = "The field contains an invalid value"
|
||||
InvalidFieldStatus int = http.StatusBadRequest
|
||||
|
||||
EntityCanNotBeDeletedCode string = "ENTITY_CAN_NOT_BE_DELETED"
|
||||
EntityCanNotBeDeletedMessage string = "The entity requested for delete can not be deleted"
|
||||
EntityCanNotBeDeletedStatus = http.StatusBadRequest
|
||||
|
||||
InvalidUuidCode string = "INVALID_UUID"
|
||||
InvalidUuidMessage string = "The supplied UUID is invalid"
|
||||
InvalidUuidStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidContentTypeCode string = "INVALID_CONTENT_TYPE"
|
||||
InvalidContentTypeMessage string = "The content type supplied is not acceptable"
|
||||
InvalidContentTypeStatus int = http.StatusBadRequest
|
||||
)
|
||||
|
||||
// specific
|
||||
const (
|
||||
InvalidAuthCode string = "INVALID_AUTH"
|
||||
InvalidAuthMessage string = "The authentication request failed"
|
||||
InvalidAuthStatus int = http.StatusUnauthorized
|
||||
|
||||
InvalidAuthMethodCode string = "INVALID_AUTH_METHOD"
|
||||
InvalidAuthMethodMessage string = "The supplied authentication method is not valid"
|
||||
InvalidAuthMethodStatus int = http.StatusBadRequest
|
||||
|
||||
EnrollmentExpiredCode string = "ENROLLMENT_EXPIRED"
|
||||
EnrollmentExpiredMessage string = "The window for this enrollment has expired"
|
||||
EnrollmentExpiredStatus int = http.StatusBadRequest
|
||||
|
||||
CouldNotProcessCsrCode string = "COULD_NOT_PROCESS_CSR"
|
||||
CouldNotProcessCsrMessage string = "The supplied csr could not be processed"
|
||||
CouldNotProcessCsrStatus int = http.StatusBadRequest
|
||||
|
||||
EnrollmentCaNoLongValidCode string = "ENROLLMENT_CA_NOT_VALID"
|
||||
EnrollmentCaNoLongValidMessage string = "The CA tied to the supplied token is no longer valid"
|
||||
EnrollmentCaNoLongValidStatus int = http.StatusBadRequest
|
||||
|
||||
EnrollmentNoValidCasCode string = "ENROLLMENT_NO_VALID_CAS"
|
||||
EnrollmentNoValidCasMessage string = "No CAs are valid for this request"
|
||||
EnrollmentNoValidCasStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidEnrollmentTokenCode string = "INVALID_ENROLLMENT_TOKEN"
|
||||
InvalidEnrollmentTokenMessage string = "The supplied token is not valid"
|
||||
InvalidEnrollmentTokenStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidEnrollMethodCode string = "INVALID_ENROLL_METHOD"
|
||||
InvalidEnrollMethodMessage string = "The supplied enrollment method is not valid"
|
||||
InvalidEnrollMethodStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidFilterCode string = "INVALID_FILTER"
|
||||
InvalidFilterMessage string = "The filter query supplied is invalid"
|
||||
InvalidFilterStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidPaginationCode string = "INVALID_PAGINATION"
|
||||
InvalidPaginationMessage string = "The pagination properties provided are invalid"
|
||||
InvalidPaginationStatus int = http.StatusBadRequest
|
||||
|
||||
NoEdgeRoutersAvailableCode string = "NO_EDGE_ROUTERS_AVAILABLE"
|
||||
NoEdgeRoutersAvailableMessage string = "No edge routers are assigned and online to handle the requested connection"
|
||||
NoEdgeRoutersAvailableStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidSortCode string = "INVALID_SORT_IDENTIFIER"
|
||||
InvalidSortMessage string = "The sort order supplied is invalid"
|
||||
InvalidSortStatus int = http.StatusBadRequest
|
||||
|
||||
CouldNotValidateCode string = "COULD_NOT_VALIDATE"
|
||||
CouldNotValidateMessage string = "The supplied request contains an invalid document"
|
||||
CouldNotValidateStatus int = http.StatusBadRequest
|
||||
|
||||
UnauthorizedCode string = "UNAUTHORIZED"
|
||||
UnauthorizedMessage string = "The request could not be completed. The session is not authorized or the credentials are invalid"
|
||||
UnauthorizedStatus int = http.StatusUnauthorized
|
||||
|
||||
CouldNotDecodeProxiedCertCode string = "COULD_NOT_PARSE_PROXY_CERT"
|
||||
CouldNotDecodeProxiedCertMessage string = "could not decode proxy client cert"
|
||||
CouldNotDecodeProxiedCertStatus int = http.StatusInternalServerError
|
||||
|
||||
CouldNotParseX509FromDerCode string = "COULD_NOT_PARSE_x509_FROM_DER"
|
||||
CouldNotParseX509FromDerMessage string = "could not parse x509 from DER"
|
||||
CouldNotParseX509FromDerStatus int = http.StatusBadRequest
|
||||
|
||||
CertFailedValidationCode string = "CERT_FAILED_VALIDATION"
|
||||
CertFailedValidationMessage string = "certificate failed to validate against CA"
|
||||
CertFailedValidationStatus int = http.StatusBadRequest
|
||||
|
||||
CertInUseCode string = "CERT_IN_USE"
|
||||
CertInUseMessage string = "The certificate supplied is already associated with another identity"
|
||||
CertInUseStatus int = http.StatusConflict
|
||||
|
||||
CaAlreadyVerifiedCode string = "CA_ALREADY_VERIFIED"
|
||||
CaAlreadyVerifiedMessage string = "CA has already been verified"
|
||||
CaAlreadyVerifiedStatus int = http.StatusConflict
|
||||
|
||||
ExpectedPemBlockCertificateCode = "EXPECTED_PEM_CERTIFICATE"
|
||||
ExpectedPemBlockCertificateMessage string = "expected PEM block type 'CERTIFICATE'"
|
||||
ExpectedPemBlockCertificateStatus int = http.StatusBadRequest
|
||||
|
||||
CouldNotParseDerBlockCode string = "COULD_NOT_PARSE_DER_BLOCK"
|
||||
CouldNotParseDerBlockMessage string = "The certificate's DER block could not be parsed"
|
||||
CouldNotParseDerBlockStatus int = http.StatusBadRequest
|
||||
|
||||
CouldNotParsePemCode string = "COULD_NOT_PARSE_PEM_BLOCK"
|
||||
CouldNotParsePemMessage string = "The certificate's PEM block could not be parsed"
|
||||
CouldNotParsePemStatus int = http.StatusBadRequest
|
||||
|
||||
InvalidCommonNameCode string = "INVALID_COMMON_NAME"
|
||||
InvalidCommonNameMessage string = "The common name of the supplied certificate is invalid"
|
||||
InvalidCommonNameStatus int = http.StatusBadRequest
|
||||
|
||||
FailedCertificateValidationCode string = "CERTIFICATE_FAILED_VALIDATION"
|
||||
FailedCertificateValidationMessage string = "The supplied certificate failed to validate against the CA's certificate chain"
|
||||
FailedCertificateValidationStatus int = http.StatusBadRequest
|
||||
|
||||
CertificateIsNotCaCode string = "CERTIFICATE_IS_NOT_CA"
|
||||
CertificateIsNotCaMessage string = "Leading certificate is not a CA"
|
||||
CertificateIsNotCaStatus int = http.StatusBadRequest
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package apierror
|
||||
|
||||
import "fmt"
|
||||
|
||||
type ValidationError struct {
|
||||
Field string `json:"field"`
|
||||
Type string `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
Message string `json:"message"`
|
||||
Details map[string]interface{} `json:"details"`
|
||||
}
|
||||
|
||||
func (e ValidationError) Error() string {
|
||||
return fmt.Sprintf("%s is invalid: %s", e.Field, e.Message)
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-foundation/identity/identity"
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
sessionTimeoutDefault = 10
|
||||
sessionTimeoutMin = 1
|
||||
|
||||
enrollmentDurationMin = 5
|
||||
enrollmentDurationDefault = 1440
|
||||
)
|
||||
|
||||
type Db struct {
|
||||
ConnectionUrl string `yaml:"connectionUrl"`
|
||||
DbName string `yaml:"dbName"`
|
||||
}
|
||||
|
||||
type Persistence struct {
|
||||
boxes []*packr.Box
|
||||
Postgres *Db `yaml:"postgres"`
|
||||
}
|
||||
|
||||
func (p *Persistence) AddMigrationBox(b *packr.Box) {
|
||||
p.boxes = append(p.boxes, b)
|
||||
}
|
||||
|
||||
func (p *Persistence) GetBoxes() []*packr.Box {
|
||||
return p.boxes
|
||||
}
|
||||
|
||||
type Enrollment struct {
|
||||
SigningCert identity.Identity
|
||||
SigningCertConfig identity.IdentityConfig
|
||||
SigningCertCaPem []byte
|
||||
EdgeIdentity EnrollmentOption
|
||||
EdgeRouter EnrollmentOption
|
||||
}
|
||||
|
||||
type EnrollmentOption struct {
|
||||
DurationMinutes time.Duration
|
||||
}
|
||||
|
||||
type Api struct {
|
||||
SessionTimeoutSeconds time.Duration
|
||||
Listener string
|
||||
Advertise string
|
||||
Identity identity.Identity
|
||||
IdentityConfig identity.IdentityConfig
|
||||
IdentityCaPem []byte
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
RootIdentityConfig identity.IdentityConfig
|
||||
RootIdentity identity.Identity
|
||||
RootIdentityCaPem []byte
|
||||
Enabled bool
|
||||
Api Api
|
||||
Enrollment Enrollment
|
||||
Persistence Persistence
|
||||
caPems [][]byte
|
||||
caPemsBuf []byte
|
||||
caPemsOnce sync.Once
|
||||
}
|
||||
|
||||
func (c *Config) SessionTimeoutDuration() time.Duration {
|
||||
return c.Api.SessionTimeoutSeconds
|
||||
}
|
||||
|
||||
func toHex(data []byte) string {
|
||||
var buf bytes.Buffer
|
||||
for i, b := range data {
|
||||
if i > 0 {
|
||||
_, _ = fmt.Fprintf(&buf, ":")
|
||||
}
|
||||
_, _ = fmt.Fprintf(&buf, "%02x", b)
|
||||
}
|
||||
return strings.ToUpper(buf.String())
|
||||
}
|
||||
|
||||
func (c *Config) CaPems() []byte {
|
||||
c.caPemsOnce.Do(func() {
|
||||
buf := bytes.Buffer{}
|
||||
//dedupe chains
|
||||
pemMap := map[string][]byte{}
|
||||
for _, caChain := range c.caPems {
|
||||
rest := caChain
|
||||
for len(rest) != 0 {
|
||||
var block *pem.Block
|
||||
block, rest = pem.Decode(rest)
|
||||
|
||||
if block != nil {
|
||||
hash := sha1.Sum(block.Bytes)
|
||||
fingerprint := toHex(hash[:])
|
||||
pemMap[fingerprint] = pem.EncodeToMemory(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for _, pemBytes := range pemMap {
|
||||
if i != 0 {
|
||||
buf.Write([]byte("\n"))
|
||||
}
|
||||
buf.Write(pemBytes)
|
||||
i++
|
||||
}
|
||||
c.caPemsBuf = buf.Bytes()
|
||||
})
|
||||
|
||||
return c.caPemsBuf
|
||||
}
|
||||
|
||||
func (c *Config) loadRootIdentity(fabricConfigMap map[interface{}]interface{}) error {
|
||||
var fabricIdentitySubMap map[interface{}]interface{}
|
||||
if value, found := fabricConfigMap["identity"]; found {
|
||||
fabricIdentitySubMap = value.(map[interface{}]interface{})
|
||||
} else {
|
||||
return errors.New("required configuration value [identity] missing")
|
||||
}
|
||||
|
||||
if value, found := fabricIdentitySubMap["cert"]; found {
|
||||
c.RootIdentityConfig.Cert = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("required configuration value [identity.cert] is missing")
|
||||
}
|
||||
|
||||
if value, found := fabricIdentitySubMap["server_cert"]; found {
|
||||
c.RootIdentityConfig.ServerCert = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("required configuration value [identity.server_cert] is missing")
|
||||
}
|
||||
|
||||
if value, found := fabricIdentitySubMap["key"]; found {
|
||||
c.RootIdentityConfig.Key = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("required configuration value [identity.key] is missing")
|
||||
}
|
||||
|
||||
if value, found := fabricIdentitySubMap["server_key"]; found {
|
||||
c.RootIdentityConfig.ServerKey = value.(string)
|
||||
} //allow "key" to be the default, this isn't an error
|
||||
|
||||
if value, found := fabricIdentitySubMap["ca"]; found {
|
||||
c.RootIdentityConfig.CA = value.(string)
|
||||
}
|
||||
|
||||
var err error
|
||||
if c.RootIdentityCaPem, err = ioutil.ReadFile(c.RootIdentityConfig.CA); err != nil {
|
||||
return fmt.Errorf("could not read file CA file from [identity.ca]")
|
||||
}
|
||||
|
||||
c.caPems = append(c.caPems, c.RootIdentityCaPem)
|
||||
|
||||
c.RootIdentity, err = identity.LoadIdentity(c.RootIdentityConfig)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Config) loadApiSection(edgeConfigMap map[interface{}]interface{}) error {
|
||||
c.Api = Api{}
|
||||
var err error
|
||||
|
||||
if value, found := edgeConfigMap["api"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
|
||||
if value, found := submap["listener"]; found {
|
||||
c.Api.Listener = value.(string)
|
||||
} else {
|
||||
return errors.New("required configuration value [edge.api.listener] missing")
|
||||
}
|
||||
|
||||
if value, found := submap["advertise"]; found {
|
||||
c.Api.Advertise = value.(string)
|
||||
} else {
|
||||
return errors.New("required configuration value [edge.api.advertise] missing")
|
||||
}
|
||||
|
||||
var intValue = 0
|
||||
if value, found := submap["sessionTimeoutMinutes"]; found {
|
||||
intValue = value.(int)
|
||||
}
|
||||
|
||||
if intValue < sessionTimeoutMin {
|
||||
intValue = sessionTimeoutDefault
|
||||
pfxlog.Logger().Warn("[edge.api.sessionTimeout] defaulted to " + strconv.Itoa(intValue))
|
||||
}
|
||||
|
||||
c.Api.SessionTimeoutSeconds = time.Duration(intValue) * time.Minute
|
||||
|
||||
var apiIdentitySubMap map[interface{}]interface{}
|
||||
if value, found = submap["identity"]; found {
|
||||
apiIdentitySubMap = value.(map[interface{}]interface{})
|
||||
}
|
||||
|
||||
if err = c.loadIApiIdentity(apiIdentitySubMap); err != nil {
|
||||
return fmt.Errorf("error loading Edge API Identity: %s", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
return errors.New("required configuration section [edge.api] missing")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) loadIApiIdentity(apiIdentitySubMap map[interface{}]interface{}) error {
|
||||
//default to root identity value
|
||||
c.Api.IdentityConfig = identity.IdentityConfig{
|
||||
Key: c.RootIdentityConfig.Key,
|
||||
Cert: c.RootIdentityConfig.Cert,
|
||||
ServerCert: c.RootIdentityConfig.ServerCert,
|
||||
ServerKey: c.RootIdentityConfig.ServerKey,
|
||||
CA: c.RootIdentityConfig.CA,
|
||||
}
|
||||
|
||||
if apiIdentitySubMap != nil {
|
||||
if value, found := apiIdentitySubMap["server_cert"]; found {
|
||||
c.Api.IdentityConfig.ServerCert = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("configuration value [edge.api.identity.server_cert] is required if [edge.api.identity] is specified")
|
||||
}
|
||||
|
||||
if value, found := apiIdentitySubMap["server_key"]; found {
|
||||
c.Api.IdentityConfig.ServerKey = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("configuration value [edge.api.identity.server_key] is required if [edge.api.identity] is specified")
|
||||
}
|
||||
|
||||
if value, found := apiIdentitySubMap["ca"]; found {
|
||||
c.Api.IdentityConfig.CA = value.(string)
|
||||
var err error
|
||||
if c.Api.IdentityCaPem, err = ioutil.ReadFile(c.Api.IdentityConfig.CA); err != nil {
|
||||
return fmt.Errorf("could not read file CA file from [edge.api.identity.ca]")
|
||||
}
|
||||
c.caPems = append(c.caPems, c.Api.IdentityCaPem)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
c.Api.Identity, err = identity.LoadIdentity(c.Api.IdentityConfig)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Config) loadEnrollmentSection(edgeConfigMap map[interface{}]interface{}) error {
|
||||
c.Enrollment = Enrollment{}
|
||||
var err error
|
||||
|
||||
if value, found := edgeConfigMap["enrollment"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
|
||||
if value, found := submap["signingCert"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
c.Enrollment.SigningCertConfig = identity.IdentityConfig{}
|
||||
|
||||
if value, found := submap["cert"]; found {
|
||||
c.Enrollment.SigningCertConfig.Cert = value.(string)
|
||||
certPem, err := ioutil.ReadFile(c.Enrollment.SigningCertConfig.Cert)
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Panic("unable to read [edge.enrollment.cert]")
|
||||
}
|
||||
//The signer is a valid trust anchor
|
||||
c.caPems = append(c.caPems, certPem)
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("required configuration value [edge.enrollment.cert] is missing")
|
||||
}
|
||||
|
||||
if value, found := submap["key"]; found {
|
||||
c.Enrollment.SigningCertConfig.Key = value.(string)
|
||||
} else {
|
||||
return fmt.Errorf("required configuration value [edge.enrollment.key] is missing")
|
||||
}
|
||||
|
||||
if value, found := submap["ca"]; found {
|
||||
c.Enrollment.SigningCertConfig.CA = value.(string)
|
||||
|
||||
if c.Enrollment.SigningCertCaPem, err = ioutil.ReadFile(c.Enrollment.SigningCertConfig.CA); err != nil {
|
||||
return fmt.Errorf("could not read file CA file from [edge.enrollment.signingCert.ca]")
|
||||
}
|
||||
|
||||
c.caPems = append(c.caPems, c.Enrollment.SigningCertCaPem)
|
||||
} //not an error if the signing cert's CA is already represented in the root [identity.ca]
|
||||
|
||||
if c.Enrollment.SigningCert, err = identity.LoadIdentity(c.Enrollment.SigningCertConfig); err != nil {
|
||||
return fmt.Errorf("error loading [edge.enrollment.signingCert]: %s", err)
|
||||
}
|
||||
|
||||
} else {
|
||||
return errors.New("required configuration section [edge.enrollment.signingCert] missing")
|
||||
}
|
||||
|
||||
if value, found := submap["edgeIdentity"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
|
||||
var edgeIdentityDurationInt = 0
|
||||
if value, found := submap["durationMinutes"]; found {
|
||||
edgeIdentityDurationInt = value.(int)
|
||||
}
|
||||
|
||||
if edgeIdentityDurationInt < enrollmentDurationMin {
|
||||
edgeIdentityDurationInt = enrollmentDurationDefault
|
||||
}
|
||||
|
||||
c.Enrollment.EdgeIdentity = EnrollmentOption{DurationMinutes: time.Duration(edgeIdentityDurationInt) * time.Minute}
|
||||
|
||||
} else {
|
||||
return errors.New("required configuration section [edge.enrollment.edgeIdentity] missing")
|
||||
}
|
||||
|
||||
if value, found := submap["edgeRouter"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
|
||||
var edgeRouterDurationInt = 0
|
||||
if value, found := submap["durationMinutes"]; found {
|
||||
edgeRouterDurationInt = value.(int)
|
||||
}
|
||||
|
||||
if edgeRouterDurationInt < enrollmentDurationMin {
|
||||
edgeRouterDurationInt = enrollmentDurationDefault
|
||||
}
|
||||
|
||||
c.Enrollment.EdgeRouter = EnrollmentOption{DurationMinutes: time.Duration(edgeRouterDurationInt) * time.Minute}
|
||||
|
||||
} else {
|
||||
return errors.New("required configuration section [edge.enrollment.edgeRouter] missing")
|
||||
}
|
||||
|
||||
} else {
|
||||
return errors.New("required configuration section [edge.enrollment] missing")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) loadPersistenceSection(edgeConfigMap map[interface{}]interface{}) error {
|
||||
c.Persistence = Persistence{Postgres: &Db{}}
|
||||
|
||||
if value, found := edgeConfigMap["persistence"]; found {
|
||||
submap := value.(map[interface{}]interface{})
|
||||
|
||||
if value, found := submap["connectionUrl"]; found {
|
||||
c.Persistence.Postgres.ConnectionUrl = value.(string)
|
||||
} else {
|
||||
return errors.New("required configuration value [edge.persistence.connectionUrl] missing")
|
||||
}
|
||||
|
||||
if value, found := submap["dbName"]; found {
|
||||
c.Persistence.Postgres.DbName = value.(string)
|
||||
} else {
|
||||
return errors.New("required configuration value [edge.persistence.dbName] missing")
|
||||
}
|
||||
|
||||
} //no longer required
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadFromMap(cfgmap map[interface{}]interface{}) (*Config, error) {
|
||||
edgeConfig := &Config{
|
||||
Enabled: false,
|
||||
}
|
||||
|
||||
var edgeConfigMap map[interface{}]interface{} = nil
|
||||
|
||||
if val, ok := cfgmap["edge"]; ok && val != nil {
|
||||
if edgeConfigMap, ok = val.(map[interface{}]interface{}); !ok {
|
||||
return nil, fmt.Errorf("expected map as edge configuration")
|
||||
}
|
||||
} else {
|
||||
return edgeConfig, nil
|
||||
}
|
||||
|
||||
edgeConfig.Enabled = cfgmap != nil
|
||||
|
||||
if !edgeConfig.Enabled {
|
||||
return edgeConfig, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if err = edgeConfig.loadRootIdentity(cfgmap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = edgeConfig.loadApiSection(edgeConfigMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = edgeConfig.loadEnrollmentSection(edgeConfigMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = edgeConfig.loadPersistenceSection(edgeConfigMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return edgeConfig, nil
|
||||
}
|
||||
|
||||
func LoadFromFile(path string) (*Config, error) {
|
||||
c := map[interface{}]interface{}{}
|
||||
|
||||
f, err := os.Open(path)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
d := yaml.NewDecoder(f)
|
||||
err = d.Decode(c)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return LoadFromMap(c)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
This project uses pakr (https://github.com/gobuffalo/packr) to embed the JSON
|
||||
schema file within the resulting binaries. Working with these files requires
|
||||
the installation of the pakr utility command.
|
||||
|
||||
- go get -u github.com/gobuffalo/packr/packr
|
||||
|
||||
The entire, edge/embedded directory structure is embedded into the controller
|
||||
binary by default.
|
||||
|
||||
|
||||
To ensure that no extra steps are needed for a consumer of this source code,
|
||||
"Building a Binary (the hard way)" as documented in the packr documentation
|
||||
is used. This means that the author of any files in the /embedded directory
|
||||
must run the packr utility command after altering/adding/removing files.
|
||||
|
||||
To run the packr utility, navigate to the /ziti project root and execute:
|
||||
|
||||
> bin/packr -z
|
||||
|
||||
It should scan the source and repack any files. "*-packr.go" files will be
|
||||
generated. These files SHOULD be checked into source (against the packr
|
||||
recommendations). This alleviates consumers from having to install and run
|
||||
the packr utility.
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/definitions.all.json",
|
||||
"definitions": {
|
||||
"tags": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^.{1,50}$": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2048
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"minLength": 1,
|
||||
"maxLength": 2048
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags-null": {
|
||||
"type": ["object", "null"],
|
||||
"additionalProperties": false,
|
||||
"patternProperties": {
|
||||
"^.{1,50}$": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2048
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"minLength": 1,
|
||||
"maxLength": 2048
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 2,
|
||||
"maxLength": 256
|
||||
},
|
||||
"name-null": {
|
||||
"type": ["string", "null"],
|
||||
"minLength": 2,
|
||||
"maxLength": 256
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"minLength": 1,
|
||||
"maxLength": 256
|
||||
},
|
||||
"id-null": {
|
||||
"type": ["string", "null"],
|
||||
"format": "uuid",
|
||||
"minLength": 1,
|
||||
"maxLength": 256
|
||||
},
|
||||
"id-str": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 256
|
||||
},
|
||||
"id-str-null": {
|
||||
"type": ["string", "null"],
|
||||
"minLength": 1,
|
||||
"maxLength": 256
|
||||
},
|
||||
"arrayOfIds": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/id"
|
||||
}
|
||||
},
|
||||
"arrayOfIds-null": {
|
||||
"type": ["array", "null"],
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/id"
|
||||
}
|
||||
},
|
||||
"arrayOfIds-str": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"$ref": "#/definitions/id-str"
|
||||
}
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 100
|
||||
},
|
||||
"username": {
|
||||
"type": "string",
|
||||
"minLength": 4,
|
||||
"maxLength": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/app-wans.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/app-wans.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"identities",
|
||||
"services"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"identities": {
|
||||
"default": [],
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
},
|
||||
"services": {
|
||||
"default": [],
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/app-wans.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"tags"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"identities": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
},
|
||||
"services": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/association.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"ids"
|
||||
],
|
||||
"properties": {
|
||||
"ids": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/association.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"ids"
|
||||
],
|
||||
"properties": {
|
||||
"ids": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/association.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"ids"
|
||||
],
|
||||
"properties": {
|
||||
"ids": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/cas.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"isAutoCaEnrollmentEnabled": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"isOttCaEnrollmentEnabled": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"isAuthEnabled": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/cas.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"certPem"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"certPem": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 20480
|
||||
},
|
||||
"isAutoCaEnrollmentEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isOttCaEnrollmentEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isAuthEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/cas.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"tags",
|
||||
"isAutoCaEnrollmentEnabled",
|
||||
"isOttCaEnrollmentEnabled",
|
||||
"isAuthEnabled"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"isAutoCaEnrollmentEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isOttCaEnrollmentEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"isAuthEnabled": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"tags": {
|
||||
"$ref": "definitions.all.json#/definitions/tags"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/clusters.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/clusters.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/clusters.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"tags"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/devices.post.json",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/edge-routers.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"clusterId": {
|
||||
"$ref": "definitions.all.json#/definitions/id-null"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/edge-routers.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"clusterId"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"clusterId": {
|
||||
"$ref": "definitions.all.json#/definitions/id"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/edge-routers.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"clusterId",
|
||||
"tags"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"clusterId": {
|
||||
"$ref": "definitions.all.json#/definitions/id"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/enroll-er.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"serverCertCsr",
|
||||
"certCsr"
|
||||
],
|
||||
"properties": {
|
||||
"certCsr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 5120
|
||||
},
|
||||
"serverCertCsr": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 5120
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/enroll-updb.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"password"
|
||||
],
|
||||
"properties": {
|
||||
"password": {
|
||||
"$ref": "definitions.all.json#/definitions/password"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/enroll.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/identities.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"type": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"enum": [
|
||||
"User",
|
||||
"Device",
|
||||
"Service"
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
},
|
||||
"isAdmin": {
|
||||
"type": [
|
||||
"boolean",
|
||||
"null"
|
||||
],
|
||||
"default": false
|
||||
},
|
||||
"permissions": {
|
||||
"type": [
|
||||
"array",
|
||||
"null"
|
||||
],
|
||||
"items": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/identities.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"permissions",
|
||||
"isAdmin"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"User",
|
||||
"Device",
|
||||
"Service"
|
||||
]
|
||||
},
|
||||
"enrollment": {
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"properties": {
|
||||
"ott": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ottca": {
|
||||
"$ref": "definitions.all.json#/definitions/id"
|
||||
},
|
||||
"updb": {
|
||||
"$ref": "definitions.all.json#/definitions/username"
|
||||
}
|
||||
}
|
||||
},
|
||||
"isAdmin": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/identities.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"tags",
|
||||
"isAdmin",
|
||||
"permissions"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"User",
|
||||
"Device",
|
||||
"Service"
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags"
|
||||
},
|
||||
"isAdmin": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/network-sessions.post.json",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"serviceId": {
|
||||
"type": "string"
|
||||
},
|
||||
"hosting": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"serviceId"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/policies.post.json",
|
||||
"type": "object"
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/services.patch.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name-null"
|
||||
},
|
||||
"dns": {
|
||||
"type": [
|
||||
"object",
|
||||
"null"
|
||||
],
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
],
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"port": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"egressRouter": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"endpointAddress": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/services.post.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"dns",
|
||||
"egressRouter",
|
||||
"endpointAddress"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"hostIds": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds-null"
|
||||
},
|
||||
"dns": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"hostname",
|
||||
"port"
|
||||
],
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"egressRouter": {
|
||||
"type": "string"
|
||||
},
|
||||
"endpointAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"clusters": {
|
||||
"$ref": "definitions.all.json#/definitions/arrayOfIds"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"$id": "http://ziti-edge.netfoundry.io/schemas/services.put.json",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"dns",
|
||||
"endpointAddress",
|
||||
"egressRouter"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "definitions.all.json#/definitions/name"
|
||||
},
|
||||
"dns": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"hostname",
|
||||
"port"
|
||||
],
|
||||
"properties": {
|
||||
"hostname": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"egressRouter": {
|
||||
"type": "string"
|
||||
},
|
||||
"endpointAddress": {
|
||||
"type": "string"
|
||||
},
|
||||
"tags": {
|
||||
"default": {},
|
||||
"$ref": "definitions.all.json#/definitions/tags-null"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
|
||||
IF EXISTS(
|
||||
SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'ziti_edge'
|
||||
)
|
||||
THEN
|
||||
EXECUTE 'DROP SCHEMA ziti_edge CASCADE';
|
||||
END IF;
|
||||
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,14 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
|
||||
IF EXISTS(
|
||||
SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'ziti_edge'
|
||||
)
|
||||
THEN
|
||||
EXECUTE 'DROP SCHEMA ziti_edge CASCADE';
|
||||
END IF;
|
||||
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE SCHEMA ziti_edge;
|
||||
@@ -0,0 +1,248 @@
|
||||
CREATE TABLE ziti_edge.geo_regions (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.cas
|
||||
(
|
||||
id uuid PRIMARY KEY NOT NULL,
|
||||
name varchar(250) NOT NULL,
|
||||
fingerprint varchar(512) NOT NULL,
|
||||
cert_pem text NOT NULL,
|
||||
is_verified boolean NOT NULL,
|
||||
verification_token varchar(512) NOT NULL,
|
||||
is_auto_ca_enrollment_enabled boolean NOT NULL,
|
||||
is_ott_ca_enrollment_enabled boolean NOT NULL,
|
||||
is_auth_enabled boolean NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX cas_fingerprint__uindex
|
||||
ON ziti_edge.cas (fingerprint);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.identity_types
|
||||
(
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(100) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX identity_types_id__uindex
|
||||
ON ziti_edge.identity_types (id);
|
||||
|
||||
CREATE TABLE ziti_edge.identities
|
||||
(
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(250) NOT NULL,
|
||||
identity_type_id UUID NOT NULL,
|
||||
is_default_admin boolean DEFAULT false NOT NULL,
|
||||
is_admin boolean DEFAULT false NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX identities_id__uindex
|
||||
ON ziti_edge.identities (id);
|
||||
|
||||
ALTER TABLE ziti_edge.identities
|
||||
ADD CONSTRAINT identities_identity_types_id__fk FOREIGN KEY (identity_type_id) REFERENCES ziti_edge.identity_types (id);
|
||||
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.sessions (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
token UUID NOT NULL,
|
||||
identity_id UUID NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.sessions
|
||||
ADD CONSTRAINT sessions_identity_id__fk FOREIGN KEY (identity_id) REFERENCES ziti_edge.identities (id);
|
||||
|
||||
CREATE UNIQUE INDEX sessions_token__uindex
|
||||
ON ziti_edge.sessions (token);
|
||||
|
||||
CREATE TABLE ziti_edge.clusters (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
CREATE TABLE ziti_edge.gateways (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
cluster_id uuid NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
fingerprint varchar(1024) NULL,
|
||||
cert_pem text NULL,
|
||||
is_verified boolean DEFAULT false NOT NULL,
|
||||
is_online boolean DEFAULT false NOT NULL,
|
||||
geo_region_id UUID NULL,
|
||||
enrollment_token UUID NULL,
|
||||
enrollment_jwt text NULL,
|
||||
enrollment_created_at timestamp with time zone NULL,
|
||||
enrollment_expires_at timestamp with time zone NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.gateways
|
||||
ADD CONSTRAINT gateways_cluster_id__fk FOREIGN KEY (cluster_id) REFERENCES ziti_edge.clusters (id);
|
||||
|
||||
ALTER TABLE ziti_edge.gateways
|
||||
ADD CONSTRAINT gateways_geo_region_id__fk FOREIGN KEY (geo_region_id) REFERENCES ziti_edge.geo_regions (id);
|
||||
|
||||
CREATE TABLE ziti_edge.protocols (
|
||||
id varchar(20) PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
CREATE TABLE ziti_edge.fabric_types (
|
||||
id varchar(20) PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.fabrics (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
fabric_type_id varchar(20) NOT NULL,
|
||||
config json NOT NULL DEFAULT '{}' :: jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.fabrics
|
||||
ADD CONSTRAINT fabrics_fabric_type_id__fk FOREIGN KEY (fabric_type_id) REFERENCES ziti_edge.fabric_types (id);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.services (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
dns_hostname varchar(2048) NOT NULL,
|
||||
dns_port integer NOT NULL,
|
||||
fabric_id UUID NOT NULL,
|
||||
fabric_config json NOT NULL DEFAULT '{}' :: jsonb,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.services
|
||||
ADD CONSTRAINT services_fabric_id__fk FOREIGN KEY (fabric_id) REFERENCES ziti_edge.fabrics (id);
|
||||
|
||||
-- Table is deprecated as of 2019-06-10
|
||||
CREATE TABLE ziti_edge.service_protocols (
|
||||
service_id UUID NOT NULL,
|
||||
protocol_id varchar(20) NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.service_protocols
|
||||
ADD CONSTRAINT service_protocols__pk PRIMARY KEY (service_id, protocol_id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_protocols
|
||||
ADD CONSTRAINT service_protocols_service_id__fk FOREIGN KEY (service_id) REFERENCES ziti_edge.services (id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_protocols
|
||||
ADD CONSTRAINT service_protocols_protocol_id__fk FOREIGN KEY (protocol_id) REFERENCES ziti_edge.protocols (id);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.service_clusters (
|
||||
service_id UUID NOT NULL,
|
||||
cluster_id UUID NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.service_clusters
|
||||
ADD CONSTRAINT service_clusters__pk PRIMARY KEY (service_id, cluster_id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_clusters
|
||||
ADD CONSTRAINT service_clusters_service_id__fk FOREIGN KEY (service_id) REFERENCES ziti_edge.services (id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_clusters
|
||||
ADD CONSTRAINT service_clusters_cluster_id__fk FOREIGN KEY (cluster_id) REFERENCES ziti_edge.clusters (id);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.network_sessions (
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
token UUID NOT NULL,
|
||||
session_id UUID NOT NULL,
|
||||
service_id UUID NOT NULL,
|
||||
cluster_id UUID NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.network_sessions
|
||||
ADD CONSTRAINT network_sessions_session_id__fk FOREIGN KEY (session_id) REFERENCES ziti_edge.sessions (id);
|
||||
|
||||
ALTER TABLE ziti_edge.network_sessions
|
||||
ADD CONSTRAINT network_sessions_service_id__fk FOREIGN KEY (service_id) REFERENCES ziti_edge.services (id);
|
||||
|
||||
ALTER TABLE ziti_edge.network_sessions
|
||||
ADD CONSTRAINT network_sessions_cluster_id__fk FOREIGN KEY (cluster_id) REFERENCES ziti_edge.clusters (id);
|
||||
|
||||
CREATE UNIQUE INDEX network_sessions_token__uindex
|
||||
ON ziti_edge.network_sessions (token);
|
||||
|
||||
CREATE TABLE ziti_edge.app_wans
|
||||
(
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE ziti_edge.app_wan_identities
|
||||
(
|
||||
app_wan_id UUID NOT NULL,
|
||||
identity_id UUID NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_identities
|
||||
ADD CONSTRAINT app_wan_identities__pk PRIMARY KEY (app_wan_id, identity_id);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_identities
|
||||
ADD CONSTRAINT app_wan_identities_identity_id__fk FOREIGN KEY (identity_id) REFERENCES ziti_edge.identities (id);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_identities
|
||||
ADD CONSTRAINT app_wan_identities_app_wan_id__fk FOREIGN KEY (app_wan_id) REFERENCES ziti_edge.app_wans (id);
|
||||
|
||||
|
||||
CREATE TABLE ziti_edge.app_wan_services
|
||||
(
|
||||
app_wan_id UUID NOT NULL,
|
||||
service_id UUID NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_services
|
||||
ADD CONSTRAINT app_wan_services__pk PRIMARY KEY (app_wan_id, service_id);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_services
|
||||
ADD CONSTRAINT app_wan_services_services_id__fk FOREIGN KEY (service_id) REFERENCES ziti_edge.services (id);
|
||||
|
||||
ALTER TABLE ziti_edge.app_wan_services
|
||||
ADD CONSTRAINT app_wan_services_app_wan_id__fk FOREIGN KEY (app_wan_id) REFERENCES ziti_edge.app_wans (id);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
INSERT INTO "ziti_edge"."identity_types" ("id", "name", "created_at", "updated_at")
|
||||
VALUES ('577104f2-1e3a-4947-a927-7383baefbc9a', 'User', DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."identity_types" ("id", "name", "created_at", "updated_at")
|
||||
VALUES ('5b53fb49-51b1-4a87-a4e4-edda9716a970', 'Device', DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."identity_types" ("id", "name", "created_at", "updated_at")
|
||||
VALUES ('c4d66f9d-fe18-4143-85d3-74329c54282b', 'Service', DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('tls', 'tls', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('quic', 'quic', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('tcp', 'tcp', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
|
||||
|
||||
INSERT INTO "ziti_edge"."fabric_types" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('zt-passthrough', 'Passthrough', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."fabric_types" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('zt-native', 'Ziti Native', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."fabrics" ("id", "name", "fabric_type_id", "config", "created_at", "updated_at", "tags")
|
||||
VALUES ('ffb41b14-d539-4db0-8651-ec1d62408023', 'Passthrough', 'zt-passthrough', DEFAULT, DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
|
||||
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('a0e2c29f-9922-4435-a8a7-5dbf7bd92377', 'Canada Central', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('ac469973-105c-4de1-9f31-fffc077487fb', 'US West', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('2360479d-cc08-4224-bd56-43baa672af30', 'Japan', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('edd0680f-3ab4-49e6-9db5-c68258ba480d', 'Australia', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('521a3db9-8140-4854-a782-61cb5d3fe043', 'South America', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('8342acad-4e49-4098-84de-829feb55d350', 'Korea', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('b5562b15-ffeb-4910-bf14-a03067f9ca2e', 'US Midwest', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('6efe28a5-744e-464d-b147-4072efb769f0', 'Canada West', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('10c6f648-92b7-49e2-be96-f62357ea572f', 'Europe West', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('72946251-1fc7-4b3b-8568-c59d4723e704', 'Africa', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('70438d63-97b3-48b2-aeb5-b066a9526456', 'Europe East', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('e339d699-7f51-4e9c-a2ca-81720e07f196', 'US South', DEFAULT, DEFAULT, DEFAULT);
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('00586703-6748-4c78-890d-efa216f21ef3', 'Canada East', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('63f7200b-7794-4a68-92aa-a36ed338ecba', 'Asia', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('f91ecca3-6f82-4c7b-8191-ea0036ce7b5a', 'US East', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('41929e78-6674-4708-89d2-9b934ea96822', 'Middle East', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."geo_regions" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('5d0042bb-6fd5-4959-90a7-6bca70e23f76', 'US Central', DEFAULT, DEFAULT, DEFAULT);
|
||||
+1
@@ -0,0 +1 @@
|
||||
DROP TABLE ziti_edge.gateway_protocols
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE ziti_edge.gateway_protocols
|
||||
(
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
gateway_id UUID NOT NULL,
|
||||
protocol_id varchar(20) NOT NULL,
|
||||
ingress_url varchar(8192) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.gateway_protocols
|
||||
ADD CONSTRAINT gateway_protocols_gateway_id__fk FOREIGN KEY (gateway_id) REFERENCES ziti_edge.gateways (id);
|
||||
|
||||
ALTER TABLE ziti_edge.gateway_protocols
|
||||
ADD CONSTRAINT gateway_protocols_protocol_id__fk FOREIGN KEY (protocol_id) REFERENCES ziti_edge.protocols (id);
|
||||
|
||||
|
||||
ALTER TABLE ziti_edge.network_sessions DROP COLUMN cluster_id;
|
||||
+1
@@ -0,0 +1 @@
|
||||
ALTER TABLE ziti_edge.gateways DROP COLUMN hostname;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE ziti_edge.gateways
|
||||
ADD hostname VARCHAR(512);
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
create table ziti_edge.enrollments
|
||||
(
|
||||
id uuid not null
|
||||
constraint enrollments_pkey
|
||||
primary key,
|
||||
identity_id uuid not null
|
||||
constraint enrollments_identity_id__fk
|
||||
references ziti_edge.identities,
|
||||
token varchar(100) not null,
|
||||
method varchar(256) not null,
|
||||
expires_at timestamp with time zone not null,
|
||||
created_at timestamp with time zone default now() not null,
|
||||
updated_at timestamp with time zone default now() not null,
|
||||
tags json default '{}'::jsonb not null
|
||||
);
|
||||
+1
@@ -0,0 +1 @@
|
||||
drop table ziti_edge.authenticators;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
create table ziti_edge.authenticators
|
||||
(
|
||||
id uuid not null
|
||||
constraint authenticators_pkey primary key,
|
||||
identity_id uuid not null
|
||||
constraint authenticators_identity_id__fk references ziti_edge.identities,
|
||||
method varchar(256) not null,
|
||||
created_at timestamp with time zone default now() not null,
|
||||
updated_at timestamp with time zone default now() not null,
|
||||
tags json default '{}' :: jsonb not null
|
||||
);
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
drop table ziti_edge.network_session_certs;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
create table ziti_edge.network_session_certs
|
||||
(
|
||||
id uuid not null
|
||||
constraint network_session_certs_pkey
|
||||
primary key,
|
||||
network_session_id uuid not null
|
||||
constraint network_session_cert_network_sessions_id__fk
|
||||
references ziti_edge.network_sessions,
|
||||
cert text not null,
|
||||
fingerprint varchar(512) not null,
|
||||
valid_from timestamp with time zone default null,
|
||||
valid_to timestamp with time zone default null,
|
||||
created_at timestamp with time zone default now() not null,
|
||||
updated_at timestamp with time zone default now() not null,
|
||||
tags json default '{}'::jsonb not null
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
drop table ziti_edge.event_logs;
|
||||
@@ -0,0 +1,17 @@
|
||||
create table ziti_edge.event_logs
|
||||
(
|
||||
id uuid not null
|
||||
constraint event_logs_pkey primary key,
|
||||
type varchar(256) not null,
|
||||
actor_type varchar(256) null,
|
||||
actor_id varchar(256) null,
|
||||
entity_type varchar(256) null,
|
||||
entity_id varchar(256) null,
|
||||
formatted_message text not null,
|
||||
format_string text not null,
|
||||
format_data text null,
|
||||
data jsonb null,
|
||||
created_at timestamp with time zone default now() not null,
|
||||
updated_at timestamp with time zone default now() not null,
|
||||
tags json default '{}' :: jsonb not null
|
||||
);
|
||||
+1
@@ -0,0 +1 @@
|
||||
drop TABLE ziti_edge.service_hosts;
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
CREATE TABLE ziti_edge.service_hosts (
|
||||
service_id UUID NOT NULL,
|
||||
identity_id UUID NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.service_hosts
|
||||
ADD CONSTRAINT service_hosts__pk PRIMARY KEY (service_id, identity_id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_hosts
|
||||
ADD CONSTRAINT service_hosts_service_id__fk FOREIGN KEY (service_id) REFERENCES ziti_edge.services (id);
|
||||
|
||||
ALTER TABLE ziti_edge.service_hosts
|
||||
ADD CONSTRAINT service_hosts_protocol_id__fk FOREIGN KEY (identity_id) REFERENCES ziti_edge.identities (id);
|
||||
|
||||
ALTER TABLE ziti_edge.network_sessions ADD COLUMN IF NOT EXISTS hosting boolean DEFAULT false;
|
||||
@@ -0,0 +1,20 @@
|
||||
alter table ziti_edge.services
|
||||
drop constraint services_fabric_id__fk;
|
||||
alter table ziti_edge.services
|
||||
drop column fabric_id;
|
||||
|
||||
alter table ziti_edge.services
|
||||
drop column fabric_config;
|
||||
|
||||
alter table ziti_edge.services
|
||||
add legacy_passthrough bool default false not null;
|
||||
|
||||
alter table ziti_edge.services
|
||||
add endpoint_address varchar(2048);
|
||||
|
||||
alter table ziti_edge.services
|
||||
add egress_router varchar(2048) not null default 'unknown';
|
||||
|
||||
|
||||
drop table ziti_edge.fabrics;
|
||||
drop table ziti_edge.fabric_types;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
alter table ziti_edge.gateways add column is_online boolean not null default false;
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
alter table ziti_edge.gateways drop column is_online;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE ziti_edge.gateway_protocols
|
||||
(
|
||||
id UUID PRIMARY KEY NOT NULL,
|
||||
gateway_id UUID NOT NULL,
|
||||
protocol_id varchar(20) NOT NULL,
|
||||
ingress_url varchar(8192) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
ALTER TABLE ziti_edge.gateway_protocols
|
||||
ADD CONSTRAINT gateway_protocols_gateway_id__fk FOREIGN KEY (gateway_id) REFERENCES ziti_edge.gateways (id);
|
||||
|
||||
ALTER TABLE ziti_edge.gateway_protocols
|
||||
ADD CONSTRAINT gateway_protocols_protocol_id__fk FOREIGN KEY (protocol_id) REFERENCES ziti_edge.protocols (id);
|
||||
|
||||
alter table ziti_edge.gateways add column hostname varchar(512)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
drop table ziti_edge.gateway_protocols;
|
||||
|
||||
alter table ziti_edge.gateways drop column hostname;
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
create table ziti_edge.service_protocols
|
||||
(
|
||||
service_id uuid not null
|
||||
constraint service_protocols_service_id__fk
|
||||
references ziti_edge.services,
|
||||
protocol_id varchar(20) not null
|
||||
constraint service_protocols_protocol_id__fk
|
||||
references ziti_edge.protocols,
|
||||
constraint service_protocols__pk
|
||||
primary key (service_id, protocol_id)
|
||||
);
|
||||
|
||||
alter table ziti_edge.service_protocols owner to postgres;
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
drop table ziti_edge.service_protocols;
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
CREATE TABLE ziti_edge.protocols (
|
||||
id varchar(20) PRIMARY KEY NOT NULL,
|
||||
name varchar(255) NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||
tags json NOT NULL DEFAULT '{}' :: jsonb
|
||||
);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('tls', 'tls', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('quic', 'quic', DEFAULT, DEFAULT, DEFAULT);
|
||||
|
||||
INSERT INTO "ziti_edge"."protocols" ("id", "name", "created_at", "updated_at", "tags")
|
||||
VALUES ('tcp', 'tcp', DEFAULT, DEFAULT, DEFAULT);
|
||||
@@ -0,0 +1 @@
|
||||
drop table ziti_edge.protocols;
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
type EntityStandardFields struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
Tags map[string]interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
type SessionEntity struct {
|
||||
Token string `json:"token"`
|
||||
EntityStandardFields
|
||||
}
|
||||
|
||||
type Empty struct {
|
||||
}
|
||||
Vendored
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by github.com/gobuffalo/packr. DO NOT EDIT.
|
||||
|
||||
package env
|
||||
|
||||
import "github.com/gobuffalo/packr"
|
||||
|
||||
// You can use the "packr clean" command to clean up this,
|
||||
// and any other packr generated files.
|
||||
func init() {
|
||||
packr.PackJSONBytes("../embedded", "README.md", "\"H4sIAAAAAAAA/3RTPW/bPBDeBeg/PPCUvIilPcM7BB3aomiGpOh8Ik8Sa4p0j8ckzq8vSCl2W6SLBop3zycfZ5dwlPiDjSInTjjSQXA1qx7Tbd9PTuc8dCYu/RSHPI7kY38kc5BraAQvA1vozPj8cP+1bZKZeSGMzjOenc4u1J/CKXt1YcLgAonj1OF7lEM5KdfKpcR1LEH4Z3bCqW3KqAtJyXtSFwPiWNdVilmdd3qCictCwXZt0zZ7TBETK/YZ/2a+fsv9x5nBQZ3wDdhO3Fc9li2sEzYa5YSkko1mYbiE838XNFYuJgaV6D1L21RxJwwnWB4pe62k2uYxgkMqK3QmRYjgFxVCUj4mkDACc9k6RgGVlSkvLKtcl5BiFlOgLN+0ze4uO2+LdYS7FfGqMJlJLJ7pdL0DJdho8sJBK9fNNXOQ83k1tG1cKqnbDrUIC1NIK8kyQVnnWGlQOG3pbMvecaptlpwUkn+H+ysl0KgsIK8sLkw92SKkF17iU1FUMbrNs3c33SDQk5tIGVsC/atTd+6wxKgoSPzCJivflmX/l96tsWP/Wk4+KdIcs7dIhlaYzeUyK1zuXlR32P23r+PdFHebE8/OewzcNhMHFtLVxUuPHz7ef/vyAQPDzGwOb6XZYK5ootLti8K2ES42cbA1nHS9pULe85Mj5XSuRsIoccFM1TWNb89kZZ/D+nb+MK77FQAA///ALYpD6wMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/definitions.all.json", "\"H4sIAAAAAAAA/+xV247aMBB95yssl8eQCwJU5Q8qVaLviEoGT4KRY0e2ozaL+PeVvUCu7AaW1b7wgJBnPOfM5WR8GCGEx4ziGOGdMXkcBC/MsAnQFHwBJpGFoKr0mQz0dgcZ0QGFhAlmmBTaJ5z7ey0F9ixOzYNjZKERwoak1cmeyxwsm9zsYWtcoLMTSl0o4X+UzEEZBjYuIVzD5VJOjAElGjfOyAjhv/4h8ubhcdwwW3BRLhMco1XNiBpXGrmJgnPccB69YZHaKCZS7LX9GRO/QaRmh2MUdb3k/8U7DWc/76IWRbYB9S3UGyk5ENFqWe20HrWtb/8nAieSiet6Vymri1ROg1k/NfPUDMKCZNC3WFrtbNQyraz1GuaLDvJ1NZ7xO2q8ncjt3Q8LSKTKiLGeomC0v7BoIN8dZT2UXht168xuwf7k1IZREaVIuUx+0d53zXmrSigkpOC2fauKkxnIWmtorMAuG/yj/r4GjOJe7VcpXK/4lEin4C/P6MqMH9wYR9ObSk60/ifVoG+rPv1F7/SjMGygFxrUPatn9j76yP6OrwEAAP//qPbbZY8JAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/app-wan.patch.json", "\"H4sIAAAAAAAA/5yOMW7DMAxFd59CUDvG0u5TdC86sBYVM1AoVaRRpEXuXlABCs8ZNOiD//33OznnXyn5xflNtS0x/pDSjOmMgVFz3Tn1W6AaZd3wChKhtfkbWEIDXbdwkcr+ZBi9NTRO/bzgqo8MUiKlylDeem3YlVD84jIUwXHQ8Wunjibw/jGSdjw0P+c8wxX/f2bcMdtUwkw8BiRAKUPmJR7SaM2Z91L86N5PD6DCWZ4DWvMInOzdp78AAAD//8KiKM5JAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/app-wan.post.json", "\"H4sIAAAAAAAA/7SQsU4DMQyG93sKKzC2yX5vwAQ76mAuTusqTULsAx1V3x0lFHSICSSGDLHyf//nnAcAc8vejGAOqmV07o2Vt+T3ZBNpyHPydbGcnUwHOqE4LGX7iklsyaL2KDmZTaPoUqhh8tORJv2YofesnBPGh5oLVWUSM0LAKNQfVHqeuVLrfxwAAEzCE/UwgGFPSbmHrhOh+sITiRkAdp1Q1uDzivF5awtWCk3NU+DUhcRijF3+xq2mrid77PLTYUX0FHCO2rR3m9/XYK243Ic7L9/Lvtb7/yrFvfztj1pym+YYr8ChncvwHgAA//9FZjxASwIAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/app-wan.put.json", "\"H4sIAAAAAAAA/6yQMU8DMQyF9/wKKzDSZL9/wAQ7YjAXp3V1TULsAx3V/XeUQKXrCOqQIdZ737Pf2QDYew52AHtQLYP3X6y8o7Anl0hjnlOoi+PsZTzQCcVjKbtPTOLKrO4oOdmHBtGlUKPktyON+jPDEFg5J5yeay5UlUnsABEnoS6o9D5zpRb/YgAAbMITdXND4l6sAXjt2rJFnDfqy69dUim2JQJFTj1aHE5TX/POb6a+O7tt/U3jQEn5iv8nItaKy1N8DHLNFaofPN6a2rv5F/HSKsBq2lvNdwAAAP//qAL8IgUCAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/association.patch.json", "\"H4sIAAAAAAAA/0yPsU4EMQxE+3yFFSgh6fcPqKBHFGbjsD6FJNi+Yjndv6PkhG6LFBmP540vDsA/cvIL+M2sLzH+svEzpS8KlSy3c02yB25R142+USOqtpXRuNXQ0dYtnLRV/zSSbO80otrniVa7aZgSDzOWN2mdxJjUL5CxKE2D0M+ZhUaHdwcA4DmpdwAfc9yPW5e74f8zDhDKA5soc50wDVjKLPYQD2pEEdxf88sNAHB1413dXwAAAP//8OXaSQgBAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/association.post.json", "\"H4sIAAAAAAAA/0yOsVIDMQxEe3+FxlCC3d8fUEHPUIizTJQ5LCMpxZHJvzN2hkkKFVqtdt85AMRHLnGBeHDvS86/7PxM5YtSI69yakX3xJJtPdA3WkYzWRmdpaUu5ulo0uLTCPK900iSzyOtftWwFB5e3N5UOqkzWVyg4mY0DUo/J1YaCO8BACBysRgAPua533+db4b/ZfAr1VFbqHKbZZZw2ybYQ75TM6ri/lpfrgUAlzDmEv4CAAD//ws9008HAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/association.put.json", "\"H4sIAAAAAAAA/0yOsU4EMQxE+3yFFSgh6fcPqKBHFGbjcD6FJNjeYjndv6PkhG4LFx6PZ97FAfhHTn4BfzLrS4y/bPxM6YtCJcttq0n2wC3qeqJv1IiqbWU0bjX0zcJZW/VPI8f2TiOofZ5ptZuGKfGwYnmT1kmMSf0CGYvSNAj9bCw0CN4dAIDnpN4BfMxzP35d7ob/ZeAL5VGbKHOdZRqwlAn2EA9qRBHcX/PLrQDg6sZc3V8AAAD//y7+fUwGAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/ca.patch.json", "\"H4sIAAAAAAAA/7SQMU/EMAyF9/4KKzDeNXu3E7oZdsTgNu41J9euEnc40P13lLaCihHBkMFPed979kcF4B5jcA24wWxqvH+PFo8ULlQLWa+zhHSro/rcDTRi9h3mekLrhvqaVdyhEOw2UUFoe6XOVg1DiBZVkF+STpQsUnYN9MiZlg/TXi5FAJzgSF9TqZaoL+BAfZQFl2tkXqIf/E71xXmUmdkt3vthBcZ8mk2f8CxJmUcSOwu2TGGfstV/3WYA16oy4brdJn2zAd5+ZDyb/XfEabbh77mGl/y7exfn/t5VeffqMwAA//+mWpq/UQIAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/ca.post.json", "\"H4sIAAAAAAAA/7SRMU/7MBDF93yKk///kSYFMaBsFeqGRHfEcI0viSvnHOyzRKn63ZFNQ0PZEAwZ8nTv9+75DgWA+m+0qkH1ImNdVW9GzIJ0RyWTtC6y9vvSuCo0PQ0YqgZDObog5S44VlcJIPuREsFtd9TIh4ZaGzGO0W68G8mLoaBqaNEGygOeXqLxlKKfCgAAxThQNgOohrxsaFAFwHMeH+eUw8ww/aUintq0h6bWcE4PJVqbN/1XzdQqO7PteBE4w021gnjD3WkzADUYfiDupFc1XJ9VfP1Ub5a3d8sveBNWUdw9rtk7awdiWTNuba7/LXDrnCXkc6KmFqOV6f0uyI8ifwNeRel/iybYhZ/dKjkXHK09HaxI37F4DwAA//+fGiQMuwIAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/ca.put.json", "\"H4sIAAAAAAAA/6RRv07zMBDf8xQnf98I8Z6tQp1hRwyX+NK4cnzBPg+l6rsjX4mIkACJDpHi8/3++twAmP/emQ7MJLJ01r558ffkDtRGkpFLdOnUerZ5mGjGbAfM7VKkPWaO5q7i5bRQJeD+SINcZ+icF88Rw1PihZJ4yqaDEUMmXUj0WnyiqvzcAACYiDMpuFLiIa//Pu+K8APuY+IQZoqyj9gHcp8LjyI/3u+KTOuwAXhRB8vW2HnjYT3VahKNNZqj0UcNlFsMQcP/s5upVaTCLr/63gis3fXMgTB+WAZVxBJk7ewL8zeBbyfeNnUrmz7in8pU5JWrqd+leQ8AAP//UVrJf6kCAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/cluster.patch.json", "\"H4sIAAAAAAAA/5SOMW4DIRBF+z0FIilt6PcU6aMUk2XwjoWBMEPhRL57NGwU0bqg4OvP++9nMca+UrCrsbtIXb3/JqEzhgu6jBJLz6HdHRXP2443YL+lzoKNXQXZdnflku1JMXKvqJzyecVNjgxCIKGSIb21UrEJIdvVREiMo9Dwq1NDFXhfjPkYYZ27qmiMzXDD/59KN4y6FjBSHhvsIKXh8+Kn1OvlOfeU7Lh9nA6gwIVnYMAIPYlGf5WnVhQ3ryz6Hr8BAAD//+eicvRgAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/cluster.post.json", "\"H4sIAAAAAAAA/5SOsW4DIRBE+/uKFUlpQ39fkT5KQY7BXgsDYZfCsfzvEWefdG0KCobhvblPROadg5nJnFXr7NwvKx8RTrAZGkvPod0sFyfLGVcvbkldFE1sLaL2IiWbw6DorWJgyvcFiz4zHwIrl+zTRysVTRliZoo+CdZCw0/nhuH/nIiITPZXmInoa32v+2/3XWO7jfkNcYgDIudVJ9antE57c7vUbWyix+HJUn+SPSsg+p50RK/KvwQDd8w9pZdlGufxFwAA//99O2dVZQEAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/cluster.put.json", "\"H4sIAAAAAAAA/5SPsU6EIRCEe55ig5YK/f8U9sYCf5a7vXCA7FKcl//dzaKX0FpQMJn5ZvZuAOwzRbuBPYu0zftvEnrFeEJXUFIdJfabo+p5P+M1sN/zYMHOrg1xF67FvihEbg2VUj8vuMuvFmIkoVpCfuu1YRdCthukkBmnoePXoI5a/24AAGwJV5xhRYYTWwPwMb1tRdwX9+Onl3RMOiJiojKr2YWc58wnv6h+JmfsWNsWVsQURhaV/iz/KniMBziMvuMnAAD//5RMgrlrAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/device.post.json", "\"H4sIAAAAAAAA/xTMUQrCMAwG4PedogQfNXnfbbT5dRnYFBOFKt599ALfbymFTqa0Ftoy+yrytbQL9AFuyLu/m74Gm0vUDc9riOJjFcHdI3kPb3SeSI6OqfhtR01a/kcAAAD//x01+a9bAAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/edge-router.patch.json", "\"H4sIAAAAAAAA/5yOsVIEIRBE8/0KCg1vId8/MDO3DHAZbueKA5wZgtO6f7eGtawNTDQgoKf7dX9OxthHjHYxdhNpi/cfKDhDPIMrIKn2EunmsHpeN7gG9nqaqXYBYteCrJu7cC32pCi5NVBWfbvAKrsWYkTBWkJ+ptqABIHtYlLIDMNA8N6RQEe8TMa8DrEdvTrTGFvCFX5+OpwgaVuEhGV0sAs5jz0P/qB6Tc6l52xH9n7agWvuLEBP8X9UjL8wJZz5iIuQQs+i0rflTx2KO7ZM+u5fAQAA//8WElk4uAEAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/edge-router.post.json", "\"H4sIAAAAAAAA/5yPsVIDIRCG+3uKHbT0oL83sLN3LPBYks0QQHYpYibv7iyeM+dYmYKCZb/v/7lOAOaRglnAHEXq4twnCc0YDmgzSiw9h3axVByvRzx7dvo0t9IFG9taWOyJSzZPapJLRVWV9xOu8j3zIZBQyT69tFKxCSGbBaJPjGOh4UenhtrhdQIAMNmfccAAZk2dBdtzMBPA2wDq3nPdIT83/VPDqE0CRsojn61PaXR9cLupG+TAbn8i7xJS+K0Tf+C9KWD0PYmOtpV/6VU3557SljLpuX0FAAD//+AN+DrJAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/edge-router.put.json", "\"H4sIAAAAAAAA/5yQsVIDIRCG+3uKHbQ00N8b2Nk7FngsyWYI4O5SxMy9uwN6M+dYmYKCn/0/PrhNAOaRgpnBnFTr7NwnKR0wHNFm1FhaDny1VJwsJ7x4cf3owKUpstja1J6lZPPUQXqt2Enl/YyLfmc+BFIq2acXLhVZCcXMEH0SHAOMH40Yu8LrBABgsr/gKAOYJTVR5OewBeqPYiaAt1Gue+ZtV992/XmMsVsFjJSHi1if0vB+cLvUjeaorX+uvwtI4TduyO9IAaNvSXv0M/Iv/PYXAOvU1/oVAAD//3HhTrXPAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/enroll-er.post.json", "\"H4sIAAAAAAAA/6yOMU/DQAyF9/wKy2JsE4rEkrUrAztiOHKviav07rBdRKn631FCgSIxdvDgT8+f37Ei4huJ3BIP7qVtmg9xWSL2qBN8k/cp6qGW3Fg3YBesQdI8jktoXbJ5vbWceDFp/FAwefLLFp1/sRCjuOQUxkfNBeoC45Y2YTTMAcXrXhRTgaeKiIgN+gZdQ31tOluIuDuvFdHzfFcudce/oW9w0clcJfVnGxHvJD0g9T5wS6tfGt5/6P3q7nbmp8V/va78o5rmVH0GAAD//7gFLdmPAQAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/enroll-updb.post.json", "\"H4sIAAAAAAAA/1yPsU5DMQxF93yFFRhpsr+vYEcMae1HXYU42H5Cpeq/owQhPXXIcnVy7/EtAMRnxrhAPLv3Jecfdj4QflBq5KtsDfWaWLKdzvRZLFNTqfWwdTymLubpYtLiyyjya6fRJMcLnfwvK4jsLK3UV5VO6kwWF1hLNZqA0tfGSkPhLQAAxF7MvkUxBoD3yfT919sD9Z+MS5TWIYC0cpuzlkqtU/Ep79K83wC4h/Hu4TcAAP//d/S3Zw8BAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/enroller.post.json", "\"H4sIAAAAAAAA/0yOvU5DMQyF9zyFFRhpst+nYEcMoXGoK9cOie9QUN8dOUjoDh58dH6+nwAQn6nGDeLFrG85f5PRCesnJkFruksd90Sa5/mCtzIzylDm1HVauk6V+OIddu/oJfpxxbP9aaVWMlIp/Dq04zDCGTdohScuw8CvnQb6+lsAeF9iP3qdDyBKueH/58QDm69VbCRrY6bCvHie8kHNnjzJzhxX9hH8HuE3AAD//6UoH7X4AAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/identity.patch.json", "\"H4sIAAAAAAAA/5SSwWrzMBCE734Kof8/OvY9t0IfoFB6Cj0o1jjZIEuutC6kxe9eVnESG0JCDj7soJn52PVvoZT+T1avld4z9+u6/iGmFewOlQe3YfA2HisKdWr26EyqycIzMSFVveFmXx1S8LqUID72kKSwPaDhk2asJabgjXuLoUcUo16r1riE/CDia6AIQdgUSn1msZ+/FUiltDcdLpNgR7TSZtGSzx2pMs5lnn/1TK3FufKDczp7x/IUOOFeAqd5M81K6cSR/E6XV+WaMqFmFX7ols6PhDj3veKbGsyVd8QsncOWaGaX5mgWrRkcizSWzy9A4m4sgNKL7cjf38E2BAfjHy/hypiPu2jqETtKSWDut5kYzfFxFzG6edLTv8M5cryBv5luUcg3FsVfAAAA//+M6eDXJQMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/identity.post.json", "\"H4sIAAAAAAAA/5ySzW7qMBCF93kKy/cuIdmzq9QHqFR1hViYeAKDnHFqjytRlHevbPLjoCwQK8ixzzdnxnMrhJD/UcudkGfmbldVv8i4BX2CkoAbG0i7a4m28vUZWuUr1ECMjODLznouL96S3EQOXzuIIHu8QM13TWmNjJaU+XC2Axd9cicaZTykCw6+AzqICfaFEEJIUi0k84gc/nfgWvQeLflRQv+mWyRZCHFItC4vcst441ds10ETY2pokFI4XypjUiP/qkytkjPZ+jxPxhpb9uyQTkMsISRQaKeOkvLlwU3nQsh3+MEacuUTXJIG5bAoDOSsMS0Qr5TPymSzHxQKxkzMKeDKoO5+5oWQ9Xi01oAiOZ31m4WvVo/O5yeNeh0bOn18nRo8uPkNE7vIf/uHNVp52LHpaXAaGhUMjzu84OQbusJSzqnrTEKG9mH6r6xmNrAs3H65PqxOi0jzxdvsfr56xG3nxYrT7Iu+KP4CAAD//0fsQ8dPBAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/identity.put.json", "\"H4sIAAAAAAAA/5yRwWrzMBCE736KRf9/TO17boU+QKH0FHJQonWywZbU3XUhDXn3IjWKZcilPRiksfabGenSAJj/5MwazFE1rrvui5Se0B2w9ah9mLzjc0uhk/0RRysdOfRKSihtnLQ9SfBmlTB6jpg4YXfCvf5o1jlSCt4Orxwichoza+jtIJgPMH5MxJgCbBoAAOPtiHm4IMvaHqSsSZ7dSL5sI/JIIhS8mAZgm8mxNrxU7LJLzRn7FNlhTz4HldYOQy71r6vULk/msWudrWKV+qJM/nCLBmDQT+O9XVbeBfn+H8C84CftsVbekLN0U7ZL43QRlbHD3k6DJum6+n2zjFsYlNt9UG4XwoDWz+1m8/ymC079LA9YltmeZxIpjvW5Pz7P3X4RbnO7wiZ916b5DgAA//8RE1iv+AIAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/network-session.post.json", "\"H4sIAAAAAAAA/2yOQU7DQAxF9zmFZbFsk31uwBkQizTz07iE8WA7oIJ6dzQJQgh1++z//v9qiPhBEvfEc0Tpu+5TQo5IZ7QZMemak11b0c7HGa+DdxnxofZydLiLZm+LerQX18yHaotrQdXp6YIxdlZMCywEzj3VTiJ22LuMeEy/6E/YwySfecO3wx6Y1aPCO+8n1QXDPmA7JEzDugT3NA2LY/c0Py42vK1iqM1P/8c0RM/N7TsAAP//mTg9SRgBAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/policy.post.json", "\"H4sIAAAAAAAA/xTMwQ1CIQwA0DtTkMajtne2UahSorSx9YDG3X9Y4P1SznCSBiVDj7BC9JWQC7cH4+S462e290JR8tr5dXUyfUoVdjT1wOE64byVWMab0dvgGpD+RwAAAP//NHeypVwAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/service.patch.json", "\"H4sIAAAAAAAA/7SSwU7EIBCG730Kgh53y70338B43XjAMm1nwwIyU5PV9N0NbK10d6MxcQ9N4c/wzf8PfFRCyHs0shFyYA6NUu/IuAXTQ+2AOz86E481ekXtAAdNiiC+YQtUB83tUO/JO7lJGD4GSBz/soeWT5o2Bhm90/Yx+gCREUg2otOWIBdEeB0xQjKwq4R4zmIoa5NFIaTTB1h2yXSELnUz0KHLPajW1mY/d6pQVTq5daO1Mp+dNiegcVTyZve7eS9WOWblGzI7zepvGXPRWc414UrerA+e+Cz3da9ZJY7o+sLvhefcdVlPRbLgI9+oS1X+v4YPfQSiJz8yxJ9v4aLf+hbWVGeCR8cPxiT8/4FZ9yuagU6PNk9smeIfXmTClS+ySt/0GQAA//9rBDXdjAMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/service.post.json", "\"H4sIAAAAAAAA/6SSvW7rMAyFdz+FoHvHxt6zdczUomuRQbVoh4EiqSRdIA387oXUxJBbB/0bDFuUzneoY54qpfR/tHqt9E4krpvmFQVXYHuoPUgXBm/pWGNouN3BwXDDQC/YAtcxsNR7Dl7fJIocIyRMeNpDK+81Yy0KBm/cPYUIJAis16ozjiEfIHgekCD5P1ZKKaW9OUAWK6Wt58sn9ATMD2EQoKnmbQzo5dbatKkrpbaZGkuzU8G9rNKlCbrUroUOfW6Sa+NcvtC/pqg2WZll49l4F1g2ln+HM0TmeNdtLK/84NycnG5cUBcyzfWvcs2HPmU79V5knGsxkOjzcjvpF1KcE8pq0SwLoe/1tDV+dLqiQy/QAxXCqnyPS5OwkFVpP16ZlG/KWjewAP35R8+pYvoZ0UJnBpdjmaL6gU3ClZNUpWd8CwAA//869whk2QMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "api-schema/entities/service.put.json", "\"H4sIAAAAAAAA/5RRTU/DMAy991dEgSNr77vxDxBXtENY3M5T5oTYRRpT/zuK90EGnYBD1frV7z0/+9AYY+/R26WxG5G07LoPFFyAH6AlkD6O5PO+xdjxegM7xx1Dfsc1cJtGabccyT4UEdknKCrxdQtrOWLOexSM5MJTjgmyILBdmt4FBm3I8DZihmL/0hhjjCW3AyUbYz3x+RPIp4gkj95n4C94KNVzHAWybYxZqWqqzQ6V7rkqmTP0ZVwPPZIOya0LQQPddRXaKVNpUzVXJTWTXPHf0mvTjw0ouoks1SYUSzGLPZWrC38m67VCjVbDsmSkwV5+Td+dbvCQBIbjsk/Epn5Pc4eZ2VVtP9248R9p4oarXg+9G4MGuIT6x7mL3ILGEE4uTXmmzwAAAP//Jaq6WigDAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180828083820_init-schema.down.sql", "\"H4sIAAAAAAAA/1SOwcrCMBCE74G8wx4C+f+LL1A81GRqAzaVJmJvJWjUHNqC9uTTSygI7mGXZfebGd2SEJztsDeWM86ITEXojfPuL2+5HA5Qnl6XRxzDMIUxUtW1DaXpNj/HsKR5GtbjZh1LoHONDj/IluQ7LWmI13uUWfo/N1/Drj7ooU4eJHXXHsmpGk1JX4JU6VSpIYv8DavJVEUODKs5E6L4BAAA//+QzumVyQAAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180828083820_init-schema.up.sql", "\"H4sIAAAAAAAA/2SOv2rDMBDGd4He4QaB2qUvYDqo0udaUMtFUqk3I1q10WAbEk95+qAYAiE33B/ufvd9ZiAhOHvDu3WccUZkW8JoQwxPdaoR8AEd6fRzyHOaljRnav3QU1n+1uOctrIu07582cuW6LuDxx3ySvJctjLl3/8s6+vnmmIHt+tghP6KIGn88ElBd+gV3QjSKmhlIJt6DWfItk01DGc4E+Laaw8V8cA2lwAAAP//BuaXKOUAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180905100347_initial-tables.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20180905100347_initial-tables.up.sql", "\"H4sIAAAAAAAA/+xa32/bNhB+D5D/gW91gGRwgmbYuicvUQZjmbO5NrA+EYxEO2xkUpDoOOmw/32wLZmk+EMyRRtDEfWlNcnj6e77vuNRvRlHg0kEJoNf7yPwjXACcTLHP8wxgzmeE0YL0Ds9AYAkoHqm0+Et+HM8/GMw/gJ+j74A8zN6mIDR9P7+fL2cogUuf39BefyE8t7V9fWZZam2PM4x4jiBiANOFrjgaJGBFeFPm3+Cb4xicBvdDab3E0DZqnemLl9mSZflHM2L0quvBaMOp+vO76x++OffD+DTp836x9OTs19OT9Z/LNGPUXF6Ug+76VkuSdIhFaZHpKffOj0zQuc4z3JCucvk9eVV+4zjnMMMLyxzOX41buUySQr4gnMyI9gY1EfGUowas6uY3NqLESeMQs6esbrc58VJAdGSMxgjiGnO0nSBKYeYosd07bePl6SAjHObRV+TaMmfJCvK42NS4rjp6cj7UCYlLTA9QfWhVIfpaPjXNALD0W30N4hRASW6QbgkNMGva9ceRqqEgJ40sUFxSIIpJ/wN8rcMG8QnkOZf9tuLyvep+aacqsGHJLFmVZ0JeiRRrFqySkwZDZJXNbv7lAzlTSBJtp60j3KlQgmeoWXKIUoWhO50p0rBDKWFodhpUrZZXD0+RjTxCiJXQQTqWPAlraBLZNgO7ifR2IlaAAa3t+DmYfR5Mh4MRxN1M402s2dw9zCOhr+NNmDu1VF2BsbRXTSORjfR5xa0clCrwEVhPB17UUo+OngRoXoBTyLJ8O2K3K6gDYpXM8Kq5BnwVQ1BKaQOXDVBihhUWiHPbr8NBKzsEXDbzHOqfpwuC47z98bt0EVca5oRxyv0ZtAE0CkHZUKhanHT+7V/I0fv55NYc8snznlXHx2mGrq8ts2du7HzPAswmhKqzvOxJC5QYJ2Be7yZ1K9JJaKDla+rXWT3jbJkpQ2rm63g14zkuPCxYu4Vw7WI4TrDI1SzSnYM1awagkJC9GImxiy1TBQU98GtjSMKL3RflGGLO8rdZEMHlOWMs5iluibvRK9/5tLj92LoWQxn6DEncXWifg/+ka+Qt+E3H0UCNP1eRwaBiHVNlDHQOomMzsh8NxYklKaCEqCSBCghR6gdJUwMil2OQDVrumKr4xbJVsVAtNfW5jp/IbFBN4KA1we7CS3gEyv41pKA7sefXMfcuoWM5eUxg1CO5zhvm1qZP2U4fNr80kLJokOxpzt5unPnKJcIW5AaLxG2Q3CXMitvGigjseXiAkzQY4oBKUCCsxzH64gBVAA2A1f9y58v+j9eXPabSAVrR6Lq5zWsZEwpwa3W1HW7mtQqUmJne8jEHAizZ4XnPeHpuexQ+J3FRnraxJglb0K7DuGa9N66b3JQzM5JuW8rwlC9TpLgskGL7aJCGWwHj2ojRyCqKW5wSA1N4G2Piox9HAvc31lBQTFfsfwZWq+/O95++1S10hffu+86ovdcXsf8vsu/y7bEDOw6eAzArk+BIrkmxlVjVsZVKHUzzs+xI0iBj2OBpcD00ULb0/3xQheN5o8YKMvgClHlw3WDtuhH/oaT/v+NiA0UdFNPK7buwELt/waIAWAo7doXxna13bSbBmF9kl7fxZxz2ZdD7H2AL39hHRQ/6f6JMYt7FbFaFPvKluh06kDRcWI7H7aDiaOnqk9xQ0RS4MDb7v5yUOH38SwwLP4LAAD//7ldsnUaLAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20181016142716_default-data.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20181016142716_default-data.up.sql", "\"H4sIAAAAAAAA/8zXzW4bNxAH8LsBv8NCFyeAp+DHkMNBT0biAmkTt6jtFujFGJJDeQFHUqVViuTpi1XswmncpI7zsSeK0OXH4fyH0rOT0+Nfz7pnJ2c/d7M3/dBfaJ3r7LtZX3Ux9MPri+H1Sjez7tGsr7PDbraQlzquZa0yaL2QYdxtV/Vm93h/77ej5+fHp92jg0BkDTYHVr0AMhIIOwLyyWfRlgvLwWF3cL7R9cFh9/T4h6Pz52f/fHj8/f7e/t4XJubgW0aGYLMFlEQgqAhaqzDZKExmJD7VV33Rb4QsWGNsXKGpTYAWPaRQPRB6xyWgSy6PyFNdf5pytV4Oy7K8ug/wsJsNMt+8Ax2uNqPj7fJvw9fG/Lnty6i5Xr81ZyirXW12y0cwHwQ1yeu+3Luh7jK9GWAlm81wuV5u55cj75d3tp9as89LXMjQvxq7+uCPfui7k5vtw3R3wG6xL95+U5aL1s/vjW4to80WoQbPgDUbSDFY0GJrdGiScf79ar9/Hf95wjv65QMHnuvyYq3zfrl46G2IUVccN2B2DhB9AElCEGpulCs7TzSe5IkspEr3RBfDWq4ecFefkV4wMpMHa0IBrGqBm7fQWiuGCBO13RA9P+1+180wCbPz0SBxhVJMAnQOIdcQAX0WieSk+d3r9KOsZDEJsdZqYjINvGQEZI3ANQcoMbmQsmAydRQfbTdjZ/QyCXVwVnzNDMmiAUwBQSg5iLbkUH1Tg7u8ni63w2V39FLXfZmGPHl0UqQCKjKg4QQJq0Jy3DSHUH3YdchPy7VOQ5xDiC7bAK1pBmRrIDeLIMabSI2LOL3O4Yu+/jWVKEZt6pIEIEQFjFghWyRAQ05bpsjN3Jp8kxkh1pTYIiZgl2kMpIOsHKFF5wOpBHJtdB9v18uVTsdNjjG6YMG2QoDZZ0ghJiiBK5LzSgZ3g6RNJotk0KcaPTBlD5iyA9EcIJsYhYOLGOKtUh/LREqt3nONzEAtWEDlAuKKQLLkjBpqluN1IncD8GPor0A2JqRIxkMkTICFEiQ2FbSJs7E5q83fSuNkSh19I2dMBiJGQIljLkVAfNTqfdKSd/+IjzYTeRkbWy1FPMSW3FjoDMmyBRVjfCxKOch1b0ymyGjZsVKCGAkByYzNUR1wZo8qHJNzo/lFX+vVhHIYqjHocobYagDkwMBGCGIuQkadb3STw//1w/rvAAAA///sRr2Y3REAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190111165646_add-gateway-protocols.down.sql", "\"H4sIAAAAAAAA/3IJ8g9QCHF08nFVqMosyYxPTUlP1UtPLEktT6yMLyjKL8lPzs8pBgQAAP//QZ+dpyYAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190111165646_add-gateway-protocols.up.sql", "\"H4sIAAAAAAAA/6ySwW7iMBCG73mKuZFIq9UupxZObmKqqMFBxjlwstzEDS4QI9s0KlXfvSJSQipKS9XOzRp/vz95JqQYMQwM3SQY9sopLotS/i2Fk7V45lujnc712nq+B6AK6CrL4ghmNJ4iuoA7vICPi6QMSJYkfzyANvQQ0+BfVx9vXQ78kzD5Uhh/+C+4FFdVaaS1fGfWHX71/3r4SUAfz40UThZcOACnNtI6sdlCrdyyOcJeVxIiPEFZwqDStR+8w3fb4ie4E6VtrR6trr7xdV3q4OV1AKNRw997wdjzUMIwvWD2ACiKIEzJnFEUEwYnV/hxtpw/rGCSUhzfkmYx/GMvAIonmGIS4vnpixZ8VfyyV29pTsV6zTNmXVCrdsatkq7WZsWttFbpykJE0xmEaZJNCeTrnXXScFWMvbcAAAD//8IbvktwAwAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190116092021_add-gateway-hostname.down.sql", "\"H4sIAAAAAAAA/3L0CXENUghxdPJxVajKLMmMT01JT9VLTyxJLU+sLFZwCfIPUHD29wn19VPIyC8uyUvMTbUGBAAA//9B3XIDNAAAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190116092021_add-gateway-hostname.up.sql", "\"H4sIAAAAAAAA/3L0CXENUghxdPJxVajKLMmMT01JT9VLTyxJLU+sLObl4nR0cVHIyC8uyUvMTVUIcwxy9nAM0jA1NNK0BgQAAP//KjPQ4zsAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190429142248_add-enrollment-table.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190429142248_add-enrollment-table.up.sql", "\"H4sIAAAAAAAA/7SS0UrrQBCG7wt5h7lrAodDFfSiPsyyzf5pxmxmw+7EmorvLrGJplC0gv53AzPffLtMGWEVpHbnQUdWNnB7/IfE4H0L0ZSt8mxFRMSOPtL3y+qbSFCS3vsTZkwZJGm0LEqLTaZrMHw2jekitzYO1GD4N1tAlHUw7P7IYrHBmKo5F4qoECEl0uK3pglGmiQ1NJBp4snGsrYxv9lsiqslJ04LrYM759ze3f+Yg+eOI5KxSqTcIqltOzqw1u8lHYPgGs7pWtzXHIfK9l5JwiEvLnP6zv0KR+0+zaqPKcjFN8yc9cvrersd23aLYygestVbAAAA//+VbEHtBQMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190502145944_add-authenticators-table.down.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1UssLclIzSvJTE4syS8qtgYEAAD//4CpwcskAAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190502145944_add-authenticators-table.up.sql", "\"H4sIAAAAAAAA/7SRQUvEMBCF74X+h3fbFsSDoIf1x4TZZLod2yYlmbh0xf8uu22lgiKC+25DHt97mbGRSRlKh55xFhXD7sj3lLVlr2JJQ0xlUZUFAIjDp3LeTr/KB4XPfT+DLrLBJ40kXvE1zowdTxijDBQndDzdrekXj05G3C3TNzHGNB0iNxzZW06bBS0m4bSUG1jbsFR6pWhbitXD41P9h3ILaT6JM6SAysBJaRhxEm2vI87BMxw3lHuFD6eq/omUR/dPJKVjWp9eUvDf/mMl7d7ed9jvr8bDZvX1c1mUxUcAAAD//y7wWpdyAgAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190525131603_add-network-session-certs.down.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1ctLLSnPL8qOL04tLs7Mz4tPTi0qKbYGBAAA//82ylRcKwAAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190525131603_add-network-session-certs.up.sql", "\"H4sIAAAAAAAA/6SRz26zQAzE70i8g28h0qdPaqVe0odZOawhLuBFtgklVd+9okRpS1Opf3xjPDP8tC6V0Akc9y3BiZ0DxZr+C/mYtAlGZpwklKRueVbkGQAARxgGjiDJQYa2XdR5yiTmiiwOVytC39D0Zp+nV+5QJ2ho+rds1slf/y6sRAscQ6iajwBKFSlJSfb1A9gZbW4Fp0e/wJwXFUtN2uuMckQtD6jF3c3tdu07YssxVJo6cO7IHLseRvbD6yeckhBEqnBor8Q8/Si03DYG9G/E0lh8gh36+LcCx9rgwZJcbJun581uN0v7d/fc3ufZSwAAAP//NcYjcYoCAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190710140702_add_event_log.down.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1UstS80ric/JTy+2BgQAAP//TM0ZTiAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190710140702_add_event_log.up.sql", "\"H4sIAAAAAAAA/6zTwU6zQBAH8HuTvsPcCsmX72Cih/owmyk7pWthluz+aaXGdzcWKmDVCPZ/g+z+dmaSyYIwhMCbQujk4IzYXP7LQRSm8HlcLpLlgojIWRqnrq9+/RT1IK2LotXek3mNCOwU1L9nqr00VAVXcmhoL82/9gKaSkbegUO245Dc3T+kE57vOM7ggxmiU73P1nAY8y1RODTDwv5sDQqbb219KBkQa0qJkXMhyDN+YXxY4/m3nokITvPzgZt4lsHdgalebw2QNk/R62YW1a6XNXwpBa6UCC4rOjrszp908ipkZct1AVJ/TNLvuqwre0sOnMerRr9s6cKtXl5XtF53E+lXOn18CwAA//8UNjN5SQQAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190711152500_add-service-hosts.down.sql", "\"H4sIAAAAAAAA/0opyi9QCHF08nFVqMosyYxPTUlP1StOLSrLTE6Nz8gvLim2BgQAAP//Ai0zFCMAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190711152500_add-service-hosts.up.sql", "\"H4sIAAAAAAAA/6yR0UrDMBiF7/sU57KD4QvsKrZ/JZilkqbgrsJcsxlaktEERZ9eVibbdOjNbnMO5/vIXyhimqDZvSB8uuSM7Xb2LtrxzW2seQ0xReQZAHy/uQ5oW15C1hqyFWI+xa6zPrn0ccgv4my2yDImNKm/MdMKK0sUtWy0YlxqXBSM2fd4UnzJ1AqPtEJ+Upqf828GPO0bs+1R1Yr4g/zJnkFRRYpkQc1vVkR+Q6P9GFLYhOGq0vkXXHc6Ntx/Vt6m9zD2JtoYXfDxKCXapQSvptPSM290g4OW8zu8hDDYtUdJFWuFxnY9RLvIvgIAAP//QNzU42ACAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190715170856_remove_fabrics.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190715170856_remove_fabrics.up.sql", "\"H4sIAAAAAAAA/5yQwUrEQBBE74H8Q99WLyLiQcjHDJ2ZnsmwY3fo7llZv142YkAPYjxX1auisDkpOM6N4L16DZQKPRjppUaycQAASCorRGFzxcoOX2rIOGuNoaYQ8nkahwOw1l8Z9vw0Dv9OR+Fcy58JmBI0KhivYUUzX1R6WWAWaZAoY28OGZsRsDhwb+0QmjitUtkDpqRkBhfUuKDePT0+v9wfQ5UbIKj0m/8bZ9+2Tz51PrO88WmrGIftqJ8dn4/Z9Ksc/LqSTR8BAAD//2RoilsZAgAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190729141132_remove-gw-status-col.down.sql", "\"H4sIAAAAAAAA/wTA0QnDMBAD0P9AdtAEXaDDBKVWjEG9g/pMaafPo0sfFE8L/1HjUOt6dJa+/E2wNbzS6x0Y88jwCOHMtBiILMSy0XRxuXDRU89927c7AAD//0gyQH5XAAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190729141132_remove-gw-status-col.up.sql", "\"H4sIAAAAAAAA/0rMKUktUihJTMpJVajKLMmMT01JT9VLTyxJLU+sLFZIKcovUEjOzynNzVPILI7Pz8vJzEu1BgQAAP//kEeLtzUAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190731134126_drop-gateway-protocols.down.sql", "\"H4sIAAAAAAAA/7TSX2vyMBQG8PtCv8O5s4WXlykMNneV2TjKujpieuFViE1Ws7WNJHEyx777sMMq6v7I2Lkrp/31afMMCEYUA0XXCYaVcopJUcj/BXdyyV/Y3Ginc11a3wt8DwBACWgny+II7kl8h8gEbvEEjk86opBmSfLvQ9jYa6kRvp89YZNqTTxzk8+4CXpn4QmCqgsjrWULU7bCRfey94WxJ+RGcicF4w7AqUpax6s5LJWbNZew0rWECA9RllCo9TII94XFXPxScLywm3iPVtcn/MkW7ry+daDfb56f+l545Xu+hxKKyU9KsSZRFMFglI4pQXFK4eAutj1wxh6eYDgiOL5Jm8IE210IBA8xwekAjw9faiFQ4i/C7XTpMN3O8pN4LbTNx0snDTg+LeWx7+BCQK7LRVXDTFtX80pC28Hzbi98DwAA///4Px00kwMAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190731134126_drop-gateway-protocols.up.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1UtPLEktT6yMLyjKL8lPzs8ptubl4uVKzClJLcKltlgBbFJyfk5pbp5CRn5xSV5ibqo1IAAA//94qxQtXwAAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190826091630_remove-service-protocol.down.sql", "\"H4sIAAAAAAAA/5SRywqDMBBF9/mKWSpIKd36MSGNow2mmTAZLfbrSyw+QOhjlsnck3OJZTSCIObqEZ5OnMamw1NCHp1FHZmELPmkCgUAsJy7BobBNRBIIAzez5d5LIUkbFwQODD0lta67ddMHsYWGYPFdLRI1by6cPLjo2F7M1xczuWfDjvKbxJr9G3xEa7jBozs7oYn6HGCYmte7XuUqqyVMl6Qv38B0CPkPYJISTrGVCv1CgAA//8X4WOAwAEAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20190826091630_remove-service-protocol.up.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1StOLSrLTE6NLyjKL8lPzs8ptgYEAAD//wnoAAMnAAAA\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20191008091800_remove-protocol.down.sql", "\"H4sIAAAAAAAA/8SQQUvDMBzF7/0Uj1zawhgi7DJPVSMUayddKuw0Yhu2yNrU9j8HE7+7pNQOIu4i6P/y+g6/8su7yXgkOER0nXAcNem1Kjdq2rSGTGF2HQIPAHSJr3uTbbGVbXB5EeIxix+ibIV7voJz6UIgzZNk0vO1rJTLz2ahC/3IF62SpMq1JJCuVEeyanDQtO0rjqZWuOV3UZ4I1OYQhA6/b8pf8SQ33SD20pn6jLfrP/7Wf//wMZ/3/LMXXnlenC55JhCnYgE2bs+mbFyfIWC6ZBMwO6DN0xC2nZ5lm5VkofcUJTlfIvBp1/kTDDFofP/4C5PXvS6sypD/6kJF06/SxxmTzwAAAP//G933TRoDAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/postgres/migrations/20191008091800_remove-protocol.up.sql", "\"H4sIAAAAAAAA/0opyi9QKElMyklVqMosyYxPTUlP1Ssoyi/JT87PKbYGBAAA//+IBLuXHwAAAA==\"")
|
||||
packr.PackJSONBytes("../embedded", "db/sqlite3/migrations/20180905100347_initial-tables.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/sqlite3/migrations/20180905100347_initial-tables.up.sql", "\"H4sIAAAAAAAA/+xazW7jNhC+B8g78LY2kEM22AAFenITpTWaOq0bF90TwUi0w12ZFEgqTlD03Qv9k9Lox5QW3XbN3ETOkJz55pvhODdrb/HoocfFD/ce2lGBJd0xwRWanZ8hxAJUjM1meYt+XS9/Waw/op+9jwgeq4dHtNrc318k4pzsaf79hUj/mcjZ1fX1vEW0Ie5LSjQNMNEoIJpqtqfo1rtbbO4fkR9LSbnGyUelyT6yReMocBXVZKfy03xSgncctn7ocod3f/397vxs/v35WfJnmdgn6vysbltoxDELRtgbGpUPLgf7YMv4jspIMq67VF6/vxruVio1jui+Za2mr+BWXSqZwi9Usi2joFGfhAgp6XWlpTLT5xPNBMdafKa2uMvFmcIk1gL7BFMuRRjuExhSTp7C5Nwup2QKC63bNLqqJLF+NrRYw0WlEcjQGBHcU6gzAh4aU5BATgGb1fK3jYeWq1vvz4QJsBFdGMeMB/Q1OdHDKplFM2MaJBMWUK6ZfsP6LaIAr0zE2e8vh/PF/4KzISMzyMCTmNk29jHkbPkfsyA7yfDLF/Ee0C2JQ41JsGe8jPDCMlsSKiCtNEgjFS6Gi5IGTYwmhtFUMAZNqca7h7W3/HGVwGJWd9ccrb07b+2tbrzfa7E8Y8G8pYJIyDlZ6hMtJE6SabNYc4JkeQJHIJmlwh+L9c1Pi7UrdYzy/BinT+lvNDMsCvmaUYVMR0NZAvB2R9YAsdHIIkP3MY4/ZB/ztv3AjaPg6SsBbqyoLPjYBbiKhGUl4iIfEaUOQgau8qfA6QV0ijZcOLoTzTkwi7XH7ZBAYYj2ZN1xmocGY75BXzCW88a7QWgN1TguL1EzJFP54dDIoGU8uJzKUILLTYfuX8h+OlTvCpfHKH2NmKTKDqmus36r4dsUTN1miaTvoGaoZyBWVCmwWeWURkzMORXSI9PQN4uDRqFrkWHh5KwJU+O+CgHpLIARP4yVpvLU0BzrXjACd0TTA3kDIhCNMnTuNWxrdMkkcE/UxXtwK7TKTlcfOlT1dD+H5pnuhqfjy13wkHF7nYum6tcDXA+zI25m1CMGIY/QYqTzY61saIFCt1/KKAX6pOAe6TSt0Wk6otPmhirC7Xxf0jVYIFgYswSt367aCoZICi18ETb5qiSEy3kXV52yQVc22JInyfysg3Wy8Jf5ATGzMZxwJ2hEOyXGyu0J85uOHuwpwbdsV86Nppoam46k0ZH8OS1x2ta2ONCOv/YWsqLyhflAjE6CIRcIBVzhZ6F0pqlC0IfvumqquoZIyDzvMa7pjsqhJjdhnJvDqQOeacjBPDGIx2F4HIS/CIJB8Ha+9lPU4loSLz4nfjOdZrdYc5k6P1mLTNTPKrUXpjRQk1QrrftUQQZKmTpNMeN2fYawn7aGHVIztL2nmpOt9zZqtKmuPaTsa2EtTvVByM+4teszsunjEvH5WVxbPnWfHSle9+qx4v/l8ggCZOGLGiALuPxLMIaaWnUsg82tJuDbmlwkivCBcKt73xMNzQTek7e/Fuj0gAYES/kV5NPcerjxnx7VBAJos9HvbSfVStGFKQYAy7l1Xm1hyRXIaM8mhWCB+Obdm5frzDWtNzdCbao4HH7rfwIAAP//CFvkqekqAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/sqlite3/migrations/20181016142716_default-data.down.sql", "\"H4sIAAAAAAAA/wEAAP//AAAAAAAAAAA=\"")
|
||||
packr.PackJSONBytes("../embedded", "db/sqlite3/migrations/20181016142716_default-data.up.sql", "\"H4sIAAAAAAAA/6SWT28bNxDF7wb8HRa+OAE8AP8MORz0ZCQ+pG3Sok5aoBdjSA7jBRxJXa0SpJ++WFlpZEdNUq0uFLXQ+817+5bSi1fXV7+97l68ev1Ld9ZXXYz9+PFm/LjS9Vn35KyvZxfd2ULe6dnT05PfL39+c3XdPTkPRNZgc2DVCyAjgbAjIJ98Fm25sJxfdOdv1jqcP/3h9OT05EhQDr5lZAg2W0BJBIKKoLUKk43CZCbQc33fF52FKlhjbFyhqU2AFj2kUD0QescloEsuT6hrHb7B6r/gXDya4+b+Wr++qdpkczfeSH3XL3af3b/fn60K2cJoIVDMgNkJsDQHwadIJVarJZxfnJ50u9f583vZ7nKSenDl+27d5y+Mw0YfbQ84l814O/krMi6Hm82q5r0I/rV+v92sdfiUylruxmldyXr9YTnUB641sQumOCA0BTC3AkmlQMOsFkuyMZQH3v53TPJFPA83B5yuhuW4LMu7r1VpvFtPTZmWIxX+2vRlktiux05RVtspymqn8EijSR768s3n4u8Rprsz3g7LzdvbSfHXve2B2b5fdyFj/14nyT/7se9e3W//U/LAU7XHmtq1r99aRpstQg2eAWs2kGKwoMXW6NAk4/xjNxfdY7sHk3ury5tB3/bLxdcMilFXHDdgdg4QfQBJQhBqbpQrO080EZ/JQqp0z3QxDnJ3yP538gpGZvJgTSiAVS1w8xZaa8UQYaK2PcDeXHd/6Ho8HuR8NEhcoRSTAJ1DyDVEQJ9FIjlpfnso/ygrWRyP0VpNTKaBl4yArBG45gAlJhdSFkymTpjLzXoKrpfjUcFZ8TUzJIsGMAUEoeQg2pJD9U0NbrtyvdyMt93lOx36MgOXPDopUgEVGdBwgoRVITlumkOoPmwD/Gk56AxMDiG6bAO0phmQrYHcLIIYbyI1LuJ0V4iXff0wqxNRm7okAQhRASNWyBYJ0JDTlilyM3tln1dAa0psEROwyzQ1w0FWjtCi84FUArk2wa42w3KlM2HkGKMLFmwrBJh9hhRighK4IjmvZHBbwzavFGTQpxo9MGUPmLID0RwgmxiFg4sY4p6pK5ljSr3nGpmBWrCAygXEFYFkyRk11CzHXTW2nd+SjuAYE1Ik4yESJsBCCRKbCtrE2dic1eb3ajHPVPSNnDEZiBgBJU4FEQHxUav3SUve/iu9XM85KxpbLUU8xJbcZClDsmxBxRgfi1IOsotunh207FgpQYyEgGSm7KoDzuxRhWNybgK97Gu9m1uIUI1BlzPEVgMgBwY2QhBzETLqfKNPhfj8Q/VPAAAA//9bssy2PQwAAA==\"")
|
||||
}
|
||||
Vendored
+508
@@ -0,0 +1,508 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/apierror"
|
||||
edgeconfig "github.com/netfoundry/ziti-edge/edge/controller/config"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/middleware"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/persistence"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/util"
|
||||
"github.com/netfoundry/ziti-edge/edge/internal/cert"
|
||||
"github.com/netfoundry/ziti-edge/edge/internal/jwt"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"github.com/netfoundry/ziti-fabric/fabric/controller/network"
|
||||
"github.com/netfoundry/ziti-fabric/fabric/xctrl"
|
||||
"github.com/netfoundry/ziti-fabric/fabric/xmgmt"
|
||||
"github.com/netfoundry/ziti-edge/sdk/ziti/config"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
jwt2 "github.com/dgrijalva/jwt-go"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/google/uuid"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/common/constants"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"gopkg.in/oleiade/reflections.v1"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AppEnv struct {
|
||||
db *gorm.DB
|
||||
dbWithPreload *gorm.DB
|
||||
Stores *migration.Stores
|
||||
BoltStores *persistence.Stores
|
||||
Handlers *model.Handlers
|
||||
Embedded *packr.Box
|
||||
Schemes *Schemes
|
||||
Config *edgeconfig.Config
|
||||
EnrollmentJwtGenerator jwt.EnrollmentGenerator
|
||||
Versions *config.Versions
|
||||
AuthHeaderName string
|
||||
AuthCookieName string
|
||||
ApiServerCsrSigner cert.Signer
|
||||
ApiClientCsrSigner cert.Signer
|
||||
ControlClientCsrSigner cert.Signer
|
||||
FingerprintGenerator cert.FingerprintGenerator
|
||||
RootRouter *mux.Router
|
||||
RequestResponderFactory response.RequestResponderFactory
|
||||
RequestResponder interface{}
|
||||
ModelHandlers *migration.ModelHandlers
|
||||
AuthRegistry model.AuthRegistry
|
||||
EnrollRegistry model.EnrollmentRegistry
|
||||
Broker *Broker
|
||||
HostController HostController
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetApiServerCsrSigner() cert.Signer {
|
||||
return ae.ApiServerCsrSigner
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetControlClientCsrSigner() cert.Signer {
|
||||
return ae.ControlClientCsrSigner
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetHostController() model.HostController {
|
||||
return ae.HostController
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetSchemas() model.Schemas {
|
||||
return ae.Schemes
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetHandlers() *model.Handlers {
|
||||
return ae.Handlers
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetConfig() *edgeconfig.Config {
|
||||
return ae.Config
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetEnrollmentJwtGenerator() jwt.EnrollmentGenerator {
|
||||
return ae.EnrollmentJwtGenerator
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetDbProvider() persistence.DbProvider {
|
||||
return ae.HostController.GetNetwork()
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetStores() *persistence.Stores {
|
||||
return ae.BoltStores
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetDbStores() *migration.Stores {
|
||||
return ae.Stores
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetAuthRegistry() model.AuthRegistry {
|
||||
return ae.AuthRegistry
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetEnrollRegistry() model.EnrollmentRegistry {
|
||||
return ae.EnrollRegistry
|
||||
}
|
||||
|
||||
func (ae *AppEnv) ClusterHasEdgeRouterOnline(clusterId string) bool {
|
||||
return ae.Broker.ClusterHasEdgeRouterOnline(clusterId)
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetApiClientCsrSigner() cert.Signer {
|
||||
return ae.ApiClientCsrSigner
|
||||
}
|
||||
|
||||
type HostController interface {
|
||||
RegisterXctrl(x xctrl.Xctrl) error
|
||||
RegisterXmgmt(x xmgmt.Xmgmt) error
|
||||
GetNetwork() *network.Network
|
||||
}
|
||||
|
||||
type Schemes struct {
|
||||
AppWan *BasicEntitySchema
|
||||
Association *BasicEntitySchema
|
||||
Ca *BasicEntitySchema
|
||||
Cluster *BasicEntitySchema
|
||||
Device *BasicEntitySchema
|
||||
Enroller *BasicEntitySchema
|
||||
EnrollEr *BasicEntitySchema
|
||||
EnrollUpdb *BasicEntitySchema
|
||||
EdgeRouter *BasicEntitySchema
|
||||
Identity *BasicEntitySchema
|
||||
NetworkSession *BasicEntitySchema
|
||||
Policy *BasicEntitySchema
|
||||
Service *BasicEntitySchema
|
||||
FabricConfigs *FabricConfigSchemas
|
||||
}
|
||||
|
||||
func (s Schemes) GetEnrollErPost() *gojsonschema.Schema {
|
||||
return s.EnrollEr.Post
|
||||
}
|
||||
|
||||
func (s Schemes) GetEnrollUpdbPost() *gojsonschema.Schema {
|
||||
return s.EnrollUpdb.Post
|
||||
}
|
||||
|
||||
type FabricConfigSchemas struct {
|
||||
Native *gojsonschema.Schema
|
||||
Passthrough *gojsonschema.Schema
|
||||
Local *gojsonschema.Schema
|
||||
}
|
||||
|
||||
type BasicEntitySchema struct {
|
||||
Post *gojsonschema.Schema
|
||||
Patch *gojsonschema.Schema
|
||||
Put *gojsonschema.Schema
|
||||
}
|
||||
|
||||
type AppHandler func(ae *AppEnv, rc *response.RequestContext)
|
||||
|
||||
type AppMiddleware func(*AppEnv, http.Handler) http.Handler
|
||||
|
||||
func NewAppEnv(c *edgeconfig.Config) *AppEnv {
|
||||
|
||||
// See README.md in /ziti/edge/embedded for details
|
||||
// This path is relative to source location of this file
|
||||
embedded := packr.NewBox("../embedded")
|
||||
|
||||
c.Persistence.AddMigrationBox(&embedded)
|
||||
|
||||
ae := &AppEnv{
|
||||
Config: c,
|
||||
db: nil,
|
||||
dbWithPreload: nil,
|
||||
Embedded: &embedded,
|
||||
Stores: nil,
|
||||
Versions: &config.Versions{
|
||||
Api: "1.0.0",
|
||||
EnrollmentApi: "1.0.0",
|
||||
},
|
||||
AuthCookieName: constants.ZitiSession,
|
||||
AuthHeaderName: constants.ZitiSession,
|
||||
RootRouter: mux.NewRouter(),
|
||||
RequestResponderFactory: response.NewRequestResponder,
|
||||
ModelHandlers: migration.GetModelHandlers(),
|
||||
AuthRegistry: &model.AuthProcessorRegistryImpl{},
|
||||
EnrollRegistry: &model.EnrollmentRegistryImpl{},
|
||||
}
|
||||
|
||||
sm := getJwtSigningMethod(c.Api.Identity.ServerCert())
|
||||
key := c.Api.Identity.ServerCert().PrivateKey
|
||||
|
||||
ae.EnrollmentJwtGenerator = jwt.NewJwtIdentityEnrollmentGenerator(ae.Config.Api.Advertise, sm, key)
|
||||
|
||||
ae.ApiClientCsrSigner = cert.NewClientSigner(ae.Config.Enrollment.SigningCert.Cert().Leaf, ae.Config.Enrollment.SigningCert.Cert().PrivateKey)
|
||||
ae.ApiServerCsrSigner = cert.NewServerSigner(ae.Config.Enrollment.SigningCert.Cert().Leaf, ae.Config.Enrollment.SigningCert.Cert().PrivateKey)
|
||||
ae.ControlClientCsrSigner = cert.NewClientSigner(ae.Config.Enrollment.SigningCert.Cert().Leaf, ae.Config.Enrollment.SigningCert.Cert().PrivateKey)
|
||||
|
||||
ae.Schemes = &Schemes{
|
||||
FabricConfigs: &FabricConfigSchemas{},
|
||||
}
|
||||
|
||||
err := ae.LoadSchemas()
|
||||
|
||||
ae.FingerprintGenerator = cert.NewFingerprintGenerator()
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("cause", err).Fatal("could not load schemas")
|
||||
}
|
||||
|
||||
return ae
|
||||
}
|
||||
|
||||
func (ae *AppEnv) InitPersistence() error {
|
||||
var err error
|
||||
|
||||
ae.db = InitPersistence(&ae.Config.Persistence)
|
||||
if ae.db != nil {
|
||||
ae.dbWithPreload = ae.db.New().Set("gorm:auto_preload", true)
|
||||
ae.Stores = migration.NewGormStores(ae.db, ae.dbWithPreload)
|
||||
}
|
||||
|
||||
ae.BoltStores, err = persistence.NewBoltStores(ae.HostController.GetNetwork())
|
||||
if err == nil {
|
||||
err = persistence.RunMigrations(ae.HostController.GetNetwork(), ae.BoltStores, ae.Stores)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
ae.Handlers = model.InitHandlers(ae)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getJwtSigningMethod(cert *tls.Certificate) jwt2.SigningMethod {
|
||||
|
||||
var sm jwt2.SigningMethod = jwt2.SigningMethodNone
|
||||
|
||||
switch cert.Leaf.PublicKey.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
key := cert.Leaf.PublicKey.(*ecdsa.PublicKey)
|
||||
switch key.Params().BitSize {
|
||||
case jwt2.SigningMethodES256.CurveBits:
|
||||
sm = jwt2.SigningMethodES256
|
||||
case jwt2.SigningMethodES384.CurveBits:
|
||||
sm = jwt2.SigningMethodES384
|
||||
case jwt2.SigningMethodES512.CurveBits:
|
||||
sm = jwt2.SigningMethodES512
|
||||
default:
|
||||
pfxlog.Logger().Panic("unsupported EC key size: ", key.Params().BitSize)
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
sm = jwt2.SigningMethodRS256
|
||||
default:
|
||||
pfxlog.Logger().Panic("unknown certificate type, unable to determine signing method")
|
||||
}
|
||||
|
||||
return sm
|
||||
}
|
||||
|
||||
func (ae *AppEnv) getSessionTokenFromRequest(r *http.Request) string {
|
||||
token := r.Header.Get(ae.AuthHeaderName)
|
||||
|
||||
if token == "" {
|
||||
sessionCookie, _ := r.Cookie(ae.AuthCookieName)
|
||||
if sessionCookie != nil {
|
||||
token = sessionCookie.Value
|
||||
}
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
func (ae *AppEnv) Db() *gorm.DB {
|
||||
return ae.db.New()
|
||||
}
|
||||
|
||||
func (ae *AppEnv) DbWithPreload() *gorm.DB {
|
||||
return ae.dbWithPreload.New()
|
||||
}
|
||||
|
||||
func (ae *AppEnv) WrapHandler(f AppHandler, prs ...permissions.Resolver) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
rid := uuid.New()
|
||||
|
||||
sw, ok := w.(*middleware.StatusWriter)
|
||||
|
||||
if ok {
|
||||
rid = sw.RequestId
|
||||
}
|
||||
|
||||
rc := &response.RequestContext{
|
||||
Id: rid,
|
||||
Identity: nil,
|
||||
Session: nil,
|
||||
ActivePermissions: []string{},
|
||||
ResponseWriter: w,
|
||||
Request: r,
|
||||
EventLogger: &DefaultEventLogger{Ae: ae},
|
||||
}
|
||||
|
||||
rc.RequestResponder = ae.RequestResponderFactory(rc)
|
||||
|
||||
sessionToken := ae.getSessionTokenFromRequest(r)
|
||||
log := pfxlog.Logger()
|
||||
_, err := uuid.Parse(sessionToken)
|
||||
|
||||
if err != nil {
|
||||
log.WithError(err).Debug("failed to parse session id")
|
||||
sessionToken = ""
|
||||
} else {
|
||||
log.Tracef("authorizing request using session id '%v'", sessionToken)
|
||||
}
|
||||
|
||||
if sessionToken != "" {
|
||||
rc.Session, err = ae.GetHandlers().ApiSession.HandleReadByToken(sessionToken)
|
||||
|
||||
if err != nil {
|
||||
//don't error on "not found", just an un-authed session, rely on permissions below
|
||||
//error on anything else as we failed to work with the store
|
||||
if !util.IsErrNotFoundErr(err) {
|
||||
log.WithError(err).Debug("error requesting session")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//updates updatedAt for session timeouts
|
||||
if rc.Session != nil {
|
||||
err := ae.GetHandlers().ApiSession.HandleUpdate(rc.Session)
|
||||
if err != nil && !util.IsErrNotFoundErr(err) {
|
||||
log.WithError(err).Debug("failed to update session activity")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if rc.Session != nil {
|
||||
rc.Identity, err = ae.GetHandlers().Identity.HandleRead(rc.Session.IdentityId)
|
||||
if err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
apiErr := apierror.NewUnauthorized()
|
||||
apiErr.Cause = fmt.Errorf("associated identity %s not found", rc.Session.IdentityId)
|
||||
apiErr.AppendCause = true
|
||||
rc.RequestResponder.RespondWithApiError(apiErr)
|
||||
} else {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if rc.Identity != nil {
|
||||
rc.ActivePermissions = append(rc.ActivePermissions, permissions.AuthenticatedPermission)
|
||||
|
||||
if rc.Identity.IsAdmin {
|
||||
rc.ActivePermissions = append(rc.ActivePermissions, permissions.AdminPermission)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pr := range prs {
|
||||
if !pr.IsAllowed(rc.ActivePermissions...) {
|
||||
rc.RequestResponder.RespondWithUnauthorizedError(rc)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
f(ae, rc)
|
||||
}
|
||||
}
|
||||
|
||||
func (ae *AppEnv) WrapMiddleware(f AppMiddleware) mux.MiddlewareFunc {
|
||||
return func(h http.Handler) http.Handler {
|
||||
return f(ae, h)
|
||||
}
|
||||
}
|
||||
|
||||
func (ae *AppEnv) GetEmbeddedFileContent(filePath string) (string, error) {
|
||||
file, err := ae.Embedded.Open(filePath)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fileContents, err := ioutil.ReadAll(file)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(fileContents), err
|
||||
}
|
||||
|
||||
func (ae *AppEnv) LoadSchemas() error {
|
||||
// add custom format checkers
|
||||
gojsonschema.FormatCheckers.Add("uuid", gojsonschema.UUIDFormatChecker{})
|
||||
|
||||
defContent, err := ae.GetEmbeddedFileContent("api-schema/definitions.all.json")
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
defLoader := gojsonschema.NewStringLoader(defContent)
|
||||
|
||||
return ae.Embedded.WalkPrefix("api-schema/entities/", func(s string, f packr.File) error {
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
fileName := filepath.Base(s)
|
||||
|
||||
fileNameParts := strings.Split(fileName, ".")
|
||||
|
||||
if len(fileNameParts) < 3 {
|
||||
return fmt.Errorf("schema file name '%s' is invalid, ensture <entity.method>.<ext> format and packing", fileName)
|
||||
}
|
||||
|
||||
entityName := fileNameParts[0]
|
||||
entityPropertyName := kebabToCamelCase(entityName)
|
||||
method := kebabToCamelCase(fileNameParts[1])
|
||||
|
||||
schemaContent, err := ioutil.ReadAll(f)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
entitySchemaLoader := gojsonschema.NewStringLoader(string(schemaContent))
|
||||
|
||||
schemaLoader := gojsonschema.NewSchemaLoader()
|
||||
|
||||
if err := schemaLoader.AddSchemas(defLoader); err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Panic("error adding definitions schema")
|
||||
}
|
||||
|
||||
schema, err := schemaLoader.Compile(entitySchemaLoader)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Panic("error compiling schema")
|
||||
}
|
||||
|
||||
if hasProp, _ := reflections.HasField(ae.Schemes, entityPropertyName); !hasProp {
|
||||
return fmt.Errorf("found schema with property name '%s' from file '%s', no matching schema property", entityPropertyName, fileName)
|
||||
}
|
||||
entitySchema, err := reflections.GetField(ae.Schemes, entityPropertyName)
|
||||
|
||||
if entitySchema == nil || reflect.ValueOf(entitySchema).IsNil() {
|
||||
entitySchema = &BasicEntitySchema{}
|
||||
err = reflections.SetField(ae.Schemes, entityPropertyName, entitySchema)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("found schema with property name '%s', could not set basic schema", entityPropertyName)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = reflections.SetField(entitySchema, method, schema)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("found schema with property name '%s', could not set methods '%s'", entityPropertyName, method)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func kebabToCamelCase(kebab string) (camelCase string) {
|
||||
isToUpper := true
|
||||
for _, runeValue := range kebab {
|
||||
if isToUpper {
|
||||
camelCase += strings.ToUpper(string(runeValue))
|
||||
isToUpper = false
|
||||
} else {
|
||||
if runeValue == '-' {
|
||||
isToUpper = true
|
||||
} else {
|
||||
camelCase += string(runeValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Vendored
+182
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"github.com/gobuffalo/packr"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/jinzhu/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
ClientCertHeader = "X-Client-CertPem"
|
||||
EdgeRouterProxyRequest = "X-Edge-Router-Proxy-Request"
|
||||
)
|
||||
|
||||
type AuthStore interface {
|
||||
BaseLoadOneByAuthenticatorId(authId string, pl *migration.Preloads) (migration.BaseDbModel, error)
|
||||
migration.Store
|
||||
}
|
||||
|
||||
type AuthModuler interface {
|
||||
Registerer
|
||||
Migrater
|
||||
Authenticator
|
||||
Enroller
|
||||
}
|
||||
|
||||
type AuthHandlerFunc func(ae *AppEnv, rc *response.RequestContext, hc *HandlerContext)
|
||||
|
||||
type HandlerContext struct {
|
||||
TargetIdentity *model.Identity
|
||||
Data map[string]interface{}
|
||||
}
|
||||
|
||||
type Migrater interface {
|
||||
GetMigrations() []*packr.Box
|
||||
}
|
||||
|
||||
type EnrollmentContextOld struct {
|
||||
Enrollment *migration.Enrollment
|
||||
Authenticator *migration.Authenticator
|
||||
Identity *migration.Identity
|
||||
Method string //dupes Enrollment.Method, but Enrollment is not always defined
|
||||
}
|
||||
|
||||
type EnrollmentContext struct {
|
||||
Enrollment *model.Enrollment
|
||||
Authenticator *model.Authenticator
|
||||
Identity *model.Identity
|
||||
Method string //dupes Enrollment.Method, but Enrollment is not always defined
|
||||
}
|
||||
|
||||
type Enroller interface {
|
||||
ProcessEnrollmentConfig(ae *AppEnv, method string, config interface{}) (interface{}, error)
|
||||
Enroll(ae *AppEnv, rc *response.RequestContext, ec *EnrollmentContextOld)
|
||||
CreateEnrollment(ae *AppEnv, ec *EnrollmentContextOld, tx *gorm.DB) error
|
||||
IsIdentityEnrollment(m string) bool
|
||||
IsEnrollerForMethod(m string) bool
|
||||
RenderEnrollmentDetails(e *migration.Enrollment) (interface{}, error)
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
IsAuthenticatorForMethod(string) bool
|
||||
IsAuthorized(*AppEnv, *http.Request) (string, error)
|
||||
RenderAuthenticatorDetails(e *migration.Authenticator) (interface{}, error)
|
||||
AuthStore() AuthStore
|
||||
Fingerprints(auth *migration.Authenticator) ([]string, error)
|
||||
}
|
||||
|
||||
type Registerer interface {
|
||||
Register(ae *AppEnv, identityRouter *mux.Router, currentUserRouter *mux.Router, idType response.IdType)
|
||||
}
|
||||
|
||||
type Registry struct {
|
||||
enrollers []Enroller
|
||||
authenticators []Authenticator
|
||||
migraters []Migrater
|
||||
registerers []Registerer
|
||||
}
|
||||
|
||||
func (r *Registry) AddModule(a AuthModuler) {
|
||||
r.AddEnroller(a)
|
||||
r.AddAuthenticator(a)
|
||||
r.AddMigrater(a)
|
||||
r.AddRegisterer(a)
|
||||
}
|
||||
|
||||
func (r *Registry) AddMigrater(m Migrater) {
|
||||
r.migraters = append(r.migraters, m)
|
||||
}
|
||||
|
||||
func (r *Registry) AddRegisterer(rer Registerer) {
|
||||
r.registerers = append(r.registerers, rer)
|
||||
}
|
||||
|
||||
func (r *Registry) AddAuthenticator(a Authenticator) {
|
||||
r.authenticators = append(r.authenticators, a)
|
||||
}
|
||||
|
||||
func (r *Registry) AddEnroller(e Enroller) {
|
||||
r.enrollers = append(r.enrollers, e)
|
||||
}
|
||||
|
||||
func (r *Registry) GetAuthenticatorByMethod(m string) Authenticator {
|
||||
for _, a := range r.authenticators {
|
||||
if a.IsAuthenticatorForMethod(m) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) GetEnrollerByMethod(m string) Enroller {
|
||||
for _, a := range r.enrollers {
|
||||
if a.IsEnrollerForMethod(m) {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Registry) GetAuthenticators() []Authenticator {
|
||||
return r.authenticators
|
||||
}
|
||||
|
||||
func (r *Registry) GetEnrollers() []Enroller {
|
||||
return r.enrollers
|
||||
}
|
||||
|
||||
func (r *Registry) GetMigraters() []Migrater {
|
||||
return r.migraters
|
||||
}
|
||||
|
||||
func (r *Registry) GetRegisterers() []Registerer {
|
||||
return r.registerers
|
||||
}
|
||||
|
||||
func WrapByIdHandler(ae *AppEnv, f AuthHandlerFunc, idType response.IdType, prs ...permissions.Resolver) http.HandlerFunc {
|
||||
return ae.WrapHandler(func(ae *AppEnv, rc *response.RequestContext) {
|
||||
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
i, err := ae.GetHandlers().Identity.HandleRead(id)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
hc := &HandlerContext{
|
||||
TargetIdentity: i,
|
||||
Data: map[string]interface{}{},
|
||||
}
|
||||
f(ae, rc, hc)
|
||||
}, prs...)
|
||||
}
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/Jeffail/gabs"
|
||||
)
|
||||
|
||||
type EnvInfo struct {
|
||||
Arch string `json:"arch"`
|
||||
Os string `json:"os"`
|
||||
OsRelease string `json:"osRelease"`
|
||||
OsVersion string `json:"osVersion"`
|
||||
}
|
||||
|
||||
type SdkInfo struct {
|
||||
Branch string `json:"branch"`
|
||||
Revision string `json:"revision"`
|
||||
Type string `json:"type"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
type Info struct {
|
||||
EnvInfo *EnvInfo `json:"envInfo"`
|
||||
SdkInfo *SdkInfo `json:"sdkInfo"`
|
||||
}
|
||||
|
||||
func ParseInfoFromBody(body []byte) (map[interface{}]interface{}, error) {
|
||||
container, err := gabs.ParseJSON(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseInfoFromContainer(container)
|
||||
}
|
||||
|
||||
func ParseInfoFromContainer(container *gabs.Container) (map[interface{}]interface{}, error) {
|
||||
info := map[interface{}]interface{}{}
|
||||
|
||||
envInfo := container.Search("envInfo")
|
||||
sdkInfo := container.Search("sdkInfo")
|
||||
|
||||
info["envInfo"] = envInfo.Data()
|
||||
info["sdkInfo"] = sdkInfo.Data()
|
||||
|
||||
return info, nil
|
||||
}
|
||||
Vendored
+874
@@ -0,0 +1,874 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-foundation/common/version"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/persistence"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"github.com/netfoundry/ziti-edge/edge/pb/edge_ctrl_pb"
|
||||
"github.com/netfoundry/ziti-fabric/fabric/controller/network"
|
||||
"github.com/netfoundry/ziti-foundation/storage/boltz"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/kataras/go-events"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/channel2"
|
||||
"github.com/pkg/errors"
|
||||
"go.etcd.io/bbolt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Broker struct {
|
||||
ae *AppEnv
|
||||
events map[events.EventEmmiter]map[events.EventName][]events.Listener
|
||||
clusterMap *clusterMap
|
||||
}
|
||||
|
||||
type clusterMap struct {
|
||||
internalClusterMap *sync.Map
|
||||
}
|
||||
|
||||
const (
|
||||
SessionRemovedType = int32(edge_ctrl_pb.ContentType_SessionRemovedType)
|
||||
SessionAddedType = int32(edge_ctrl_pb.ContentType_SessionAddedType)
|
||||
SessionUpdatedType = int32(edge_ctrl_pb.ContentType_SessionUpdatedType)
|
||||
|
||||
ApiSessionHeartbeatType = int32(edge_ctrl_pb.ContentType_ApiSessionHeartbeatType)
|
||||
ApiSessionRemovedType = int32(edge_ctrl_pb.ContentType_ApiSessionRemovedType)
|
||||
ApiSessionAddedType = int32(edge_ctrl_pb.ContentType_ApiSessionAddedType)
|
||||
ApiSessionUpdatedType = int32(edge_ctrl_pb.ContentType_ApiSessionUpdatedType)
|
||||
|
||||
ServerHelloType = int32(edge_ctrl_pb.ContentType_ServerHelloType)
|
||||
ClientHelloType = int32(edge_ctrl_pb.ContentType_ClientHelloType)
|
||||
ErrorType = int32(edge_ctrl_pb.ContentType_ErrorType)
|
||||
)
|
||||
|
||||
func (m *clusterMap) getEntryMap(clusterId string) *sync.Map {
|
||||
if value, ok := m.internalClusterMap.Load(clusterId); ok {
|
||||
edgeRouterMap, ok := value.(*sync.Map)
|
||||
|
||||
if !ok {
|
||||
panic("could not convert edge router map to *sync.Map")
|
||||
}
|
||||
|
||||
return edgeRouterMap
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *clusterMap) AddEntry(clusterId string, edgeRouterEntry *edgeRouterEntry) {
|
||||
edgeRouterMap := m.getEntryMap(clusterId)
|
||||
|
||||
if edgeRouterMap == nil {
|
||||
edgeRouterMap = &sync.Map{}
|
||||
m.internalClusterMap.Store(clusterId, edgeRouterMap)
|
||||
}
|
||||
|
||||
edgeRouterMap.Store(edgeRouterEntry.EdgeRouter.Id, edgeRouterEntry)
|
||||
}
|
||||
|
||||
func (m *clusterMap) GetEntries(clusterId string) []*edgeRouterEntry {
|
||||
var entries []*edgeRouterEntry
|
||||
|
||||
entryMap := m.getEntryMap(clusterId)
|
||||
|
||||
if entryMap == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
entryMap.Range(func(_, vi interface{}) bool {
|
||||
if entry, ok := vi.(*edgeRouterEntry); ok {
|
||||
entries = append(entries, entry)
|
||||
} else {
|
||||
panic("cluster/edge router map contains a non *edgeRouterEntry value")
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func (m *clusterMap) GetOnlineEntries(clusterId string) []*edgeRouterEntry {
|
||||
var entries []*edgeRouterEntry
|
||||
|
||||
for _, entry := range m.GetEntries(clusterId) {
|
||||
if !entry.Channel.IsClosed() {
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func (m *clusterMap) RemoveEntry(clusterId, edgeRouterId string) {
|
||||
if v, ok := m.internalClusterMap.Load(clusterId); ok {
|
||||
if edgeRouterMap, ok := v.(*sync.Map); ok {
|
||||
edgeRouterMap.Delete(edgeRouterId)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (m *clusterMap) RangeEdgeRouterEntries(f func(entries *edgeRouterEntry) bool) {
|
||||
m.internalClusterMap.Range(func(clusterId, value interface{}) bool {
|
||||
if edgeRouterMap, ok := value.(*sync.Map); ok {
|
||||
edgeRouterMap.Range(func(edgeRouterId, value interface{}) bool {
|
||||
if edgeRouterEntry, ok := value.(*edgeRouterEntry); ok {
|
||||
return f(edgeRouterEntry)
|
||||
}
|
||||
pfxlog.Logger().Panic("could not convert edge router entry")
|
||||
return false
|
||||
})
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
type edgeRouterEntry struct {
|
||||
EdgeRouter *model.EdgeRouter
|
||||
Channel channel2.Channel
|
||||
}
|
||||
|
||||
func listener2async(l events.Listener) events.Listener {
|
||||
return func(args ...interface{}) {
|
||||
go l(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func NewBroker(ae *AppEnv) *Broker {
|
||||
b := &Broker{
|
||||
ae: ae,
|
||||
clusterMap: &clusterMap{
|
||||
internalClusterMap: &sync.Map{},
|
||||
},
|
||||
}
|
||||
|
||||
b.events = map[events.EventEmmiter]map[events.EventName][]events.Listener{
|
||||
b.ae.GetStores().Session: {
|
||||
boltz.EventDelete: []events.Listener{
|
||||
b.sessionDeleteEventHandler,
|
||||
},
|
||||
boltz.EventCreate: []events.Listener{
|
||||
b.sessionCreateEventHandler,
|
||||
},
|
||||
},
|
||||
b.ae.GetStores().ApiSession: {
|
||||
boltz.EventCreate: []events.Listener{
|
||||
b.apiSessionCreateEventHandler,
|
||||
},
|
||||
boltz.EventDelete: []events.Listener{
|
||||
b.apiSessionDeleteEventHandler,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
b.registerEventHandlers()
|
||||
|
||||
ae.HostController.GetNetwork().AddRouterPresenceHandler(b)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Broker) AddEdgeRouter(ch channel2.Channel, edgeRouter *model.EdgeRouter) {
|
||||
if edgeRouter.ClusterId == "" {
|
||||
pfxlog.Logger().Errorf("adding connecting edge router failed, edge router [%s] did not have a cluster id [%s]", edgeRouter.Id, edgeRouter.ClusterId)
|
||||
return
|
||||
}
|
||||
|
||||
edgeRouterEntry := &edgeRouterEntry{
|
||||
EdgeRouter: edgeRouter,
|
||||
Channel: ch,
|
||||
}
|
||||
|
||||
b.clusterMap.AddEntry(edgeRouter.ClusterId, edgeRouterEntry)
|
||||
|
||||
sessionMsg, err := b.getCurrentSessions()
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("could not get current sessions")
|
||||
return
|
||||
}
|
||||
|
||||
pfxlog.Logger().Infof("sending edge router session updates [%d]", len(sessionMsg.ApiSessions))
|
||||
|
||||
if buf, err := proto.Marshal(sessionMsg); err == nil {
|
||||
if err = ch.Send(channel2.NewMessage(ApiSessionAddedType, buf)); err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending session added")
|
||||
}
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending session added, could not marshal message content")
|
||||
}
|
||||
|
||||
networkSessionMsg, err := b.getCurrentStateNetworkSessions(edgeRouter.ClusterId)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Errorf("could not get current network sessions for cluster id [%s]", edgeRouter.ClusterId)
|
||||
return
|
||||
}
|
||||
|
||||
pfxlog.Logger().Infof("sending edge router network session updates [%d]", len(networkSessionMsg.Sessions))
|
||||
|
||||
if buf, err := proto.Marshal(networkSessionMsg); err == nil {
|
||||
if err = ch.Send(channel2.NewMessage(SessionAddedType, buf)); err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session added")
|
||||
}
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session added, could not marshal message content")
|
||||
}
|
||||
|
||||
pfxlog.Logger().Infof("edge router connection finalized and synchronized [%s] [%s]", edgeRouter.Id, edgeRouter.Name)
|
||||
}
|
||||
|
||||
func (b *Broker) getEventDetails(args []interface{}) *migration.CrudEventDetails {
|
||||
if args != nil && args[0] != nil {
|
||||
if ed, ok := args[0].(*migration.CrudEventDetails); ok {
|
||||
return ed
|
||||
}
|
||||
} else {
|
||||
log := pfxlog.Logger()
|
||||
log.Warn("event fired without the expected CrudEventDetails pointer as args[0]")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Broker) apiSessionCreateEventHandler(args ...interface{}) {
|
||||
var apiSession *persistence.ApiSession
|
||||
if len(args) == 1 {
|
||||
apiSession, _ = args[0].(*persistence.ApiSession)
|
||||
}
|
||||
|
||||
if apiSession == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
b.sendApiSessionCreates(apiSession)
|
||||
}
|
||||
|
||||
func (b *Broker) sendApiSessionCreates(apiSessions ...*persistence.ApiSession) {
|
||||
apiSessionMsg := &edge_ctrl_pb.ApiSessionAdded{}
|
||||
|
||||
apiSessionMsg.IsFullState = false
|
||||
|
||||
for _, session := range apiSessions {
|
||||
fingerprints, err := b.getApiSessionFingerprints(session.IdentityId)
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could not get session fingerprints")
|
||||
return
|
||||
}
|
||||
|
||||
apiSessionMsg.ApiSessions = append(apiSessionMsg.ApiSessions, &edge_ctrl_pb.ApiSession{
|
||||
Token: session.Token,
|
||||
CertFingerprints: fingerprints,
|
||||
})
|
||||
}
|
||||
|
||||
byteMsg, err := proto.Marshal(apiSessionMsg)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could not marshal api session added message")
|
||||
return
|
||||
}
|
||||
|
||||
channelMsg := channel2.NewMessage(ApiSessionAddedType, byteMsg)
|
||||
|
||||
b.sendToAllEdgeRouters(channelMsg)
|
||||
}
|
||||
|
||||
func (b *Broker) apiSessionUpdateEventHandler(args ...interface{}) {
|
||||
var apiSession *persistence.ApiSession
|
||||
if len(args) == 1 {
|
||||
apiSession, _ = args[0].(*persistence.ApiSession)
|
||||
}
|
||||
|
||||
if apiSession == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
b.sendApiSessionUpdates(apiSession)
|
||||
}
|
||||
|
||||
func (b *Broker) sendApiSessionUpdates(sessions ...*persistence.ApiSession) {
|
||||
apiSessionMsg := &edge_ctrl_pb.ApiSessionUpdated{}
|
||||
|
||||
for _, session := range sessions {
|
||||
fingerprints, err := b.getApiSessionFingerprints(session.IdentityId)
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could not get session fingerprints")
|
||||
return
|
||||
}
|
||||
|
||||
apiSessionMsg.ApiSessions = append(apiSessionMsg.ApiSessions, &edge_ctrl_pb.ApiSession{
|
||||
Token: session.Token,
|
||||
CertFingerprints: fingerprints,
|
||||
})
|
||||
}
|
||||
|
||||
byteMsg, err := proto.Marshal(apiSessionMsg)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could not marshal session updated message")
|
||||
return
|
||||
}
|
||||
|
||||
channelMsg := channel2.NewMessage(ApiSessionUpdatedType, byteMsg)
|
||||
|
||||
b.sendToAllEdgeRouters(channelMsg)
|
||||
}
|
||||
|
||||
func (b *Broker) sendToAllEdgeRouters(msg *channel2.Message) {
|
||||
b.clusterMap.RangeEdgeRouterEntries(func(edgeRouterEntry *edgeRouterEntry) bool {
|
||||
err := edgeRouterEntry.Channel.Send(msg)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could send session added message")
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (b *Broker) apiSessionDeleteEventHandler(args ...interface{}) {
|
||||
var apiSession *persistence.ApiSession
|
||||
if len(args) == 1 {
|
||||
apiSession, _ = args[0].(*persistence.ApiSession)
|
||||
}
|
||||
|
||||
if apiSession == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
b.sendApiSessionDeletes(apiSession)
|
||||
}
|
||||
|
||||
func (b *Broker) sendApiSessionDeletes(sessions ...*persistence.ApiSession) {
|
||||
apiSessionMsg := &edge_ctrl_pb.ApiSessionRemoved{}
|
||||
|
||||
for _, session := range sessions {
|
||||
|
||||
apiSessionMsg.Tokens = append(apiSessionMsg.Tokens, session.Token)
|
||||
}
|
||||
|
||||
byteMsg, err := proto.Marshal(apiSessionMsg)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithError(err).Errorf("could not marshal session removed message")
|
||||
return
|
||||
}
|
||||
|
||||
channelMsg := channel2.NewMessage(ApiSessionRemovedType, byteMsg)
|
||||
|
||||
b.sendToAllEdgeRouters(channelMsg)
|
||||
}
|
||||
|
||||
func (b *Broker) syncIdentitiesEventHandler(eventName events.EventName, args ...interface{}) {
|
||||
ed := b.getEventDetails(args)
|
||||
|
||||
if ed == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
|
||||
err := b.ae.GetDbProvider().GetDb().Update(func(tx *bbolt.Tx) error {
|
||||
ctx := boltz.NewMutateContext(tx)
|
||||
for _, e := range ed.Entities {
|
||||
if eventName == migration.EventCreate || eventName == migration.EventUpdate {
|
||||
log.Debugf("creating bolt identity to mirror gorm identity: %v", e.GetId())
|
||||
gormIdentity, ok := e.(*migration.Identity)
|
||||
if !ok {
|
||||
return errors.Errorf("unable to cast event entity to Identity. id: %v, type: %v", e.GetId(), reflect.TypeOf(e))
|
||||
}
|
||||
identity := &persistence.Identity{
|
||||
BaseEdgeEntityImpl: persistence.BaseEdgeEntityImpl{
|
||||
Id: gormIdentity.ID,
|
||||
EdgeEntityFields: persistence.EdgeEntityFields{
|
||||
CreatedAt: *gormIdentity.CreatedAt,
|
||||
UpdatedAt: *gormIdentity.UpdatedAt,
|
||||
Tags: *gormIdentity.Tags,
|
||||
Migrate: true,
|
||||
},
|
||||
},
|
||||
Name: *gormIdentity.Name,
|
||||
IsDefaultAdmin: *gormIdentity.IsDefaultAdmin,
|
||||
IsAdmin: *gormIdentity.IsAdmin,
|
||||
}
|
||||
if eventName == migration.EventCreate {
|
||||
if err := b.ae.GetStores().Identity.Create(ctx, identity); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := b.ae.GetStores().Identity.Update(ctx, identity, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if eventName == migration.EventDelete {
|
||||
log.Debugf("deleting bolt identity to mirror gorm identity: %v", e.GetId())
|
||||
if err := b.ae.GetStores().Identity.DeleteById(ctx, e.GetId()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Failure while syncing identities from gorm to bolt: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) sessionDeleteEventHandler(args ...interface{}) {
|
||||
var session *persistence.Session
|
||||
if len(args) == 1 {
|
||||
session, _ = args[0].(*persistence.Session)
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
b.sendSessionDeletes(session)
|
||||
}
|
||||
|
||||
func (b *Broker) sendSessionDeletes(sessions ...*persistence.Session) {
|
||||
removedByClusterId := map[string]*edge_ctrl_pb.SessionRemoved{}
|
||||
|
||||
for _, session := range sessions {
|
||||
service, err := b.ae.Handlers.Service.HandleRead(session.ServiceId)
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("cause", err).Error("could not send network session removed, could not find service")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, clusterId := range service.Clusters {
|
||||
var ok bool
|
||||
if _, ok = removedByClusterId[clusterId]; !ok {
|
||||
removedByClusterId[clusterId] = &edge_ctrl_pb.SessionRemoved{}
|
||||
}
|
||||
|
||||
removedByClusterId[clusterId].Tokens = append(removedByClusterId[clusterId].Tokens, session.Token)
|
||||
}
|
||||
}
|
||||
|
||||
for clusterId, nsr := range removedByClusterId {
|
||||
entries := b.clusterMap.GetOnlineEntries(clusterId)
|
||||
for _, e := range entries {
|
||||
|
||||
if buf, err := proto.Marshal(nsr); err == nil {
|
||||
if err = e.Channel.Send(channel2.NewMessage(SessionRemovedType, buf)); err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session removed")
|
||||
}
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session removed, could not marshal message content")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) sessionCreateEventHandler(args ...interface{}) {
|
||||
var session *persistence.Session
|
||||
if len(args) == 1 {
|
||||
session, _ = args[0].(*persistence.Session)
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error("could not cast event args to event details")
|
||||
return
|
||||
}
|
||||
|
||||
b.sendSessionCreates(session)
|
||||
}
|
||||
|
||||
func (b *Broker) sendSessionCreates(sessions ...*persistence.Session) {
|
||||
addByClusterId := map[string]*edge_ctrl_pb.SessionAdded{}
|
||||
|
||||
for _, session := range sessions {
|
||||
service, err := b.ae.Handlers.Service.HandleRead(session.ServiceId)
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("cause", err).Error("could not send network session added, could not find service")
|
||||
continue
|
||||
}
|
||||
|
||||
for _, clusterId := range service.Clusters {
|
||||
var ok bool
|
||||
if _, ok = addByClusterId[clusterId]; !ok {
|
||||
addByClusterId[clusterId] = &edge_ctrl_pb.SessionAdded{}
|
||||
}
|
||||
|
||||
fps, err := b.getActiveFingerprints(session.Id)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().Errorf("could not obtain a fingerprint for the api session [%s] and session [%s]", session.ApiSessionId, session.Id)
|
||||
continue
|
||||
}
|
||||
|
||||
svc, err := b.modelServiceToProto(service)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().Errorf("could not convert service [%s] to proto: %s", service.Id, err)
|
||||
continue
|
||||
}
|
||||
|
||||
addByClusterId[clusterId].Sessions = append(addByClusterId[clusterId].Sessions, &edge_ctrl_pb.Session{
|
||||
Token: session.Token,
|
||||
Service: svc,
|
||||
CertFingerprints: fps,
|
||||
Hosting: session.IsHosting,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for clusterId, nsr := range addByClusterId {
|
||||
entries := b.clusterMap.GetOnlineEntries(clusterId)
|
||||
for _, e := range entries {
|
||||
|
||||
if buf, err := proto.Marshal(nsr); err == nil {
|
||||
if err = e.Channel.Send(channel2.NewMessage(SessionAddedType, buf)); err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session added")
|
||||
}
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("error sending network session added, could not marshal message content")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) modelServiceToProto(service *model.Service) (*edge_ctrl_pb.Service, error) {
|
||||
return &edge_ctrl_pb.Service{
|
||||
Name: service.Name,
|
||||
Id: service.Id,
|
||||
EndpointAddress: service.EndpointAddress,
|
||||
EgressRouter: service.EgressRouter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Broker) registerEventHandlers() {
|
||||
for s, enls := range b.events {
|
||||
for en, ls := range enls {
|
||||
for _, l := range ls {
|
||||
s.AddListener(en, l)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) getCurrentSessions() (*edge_ctrl_pb.ApiSessionAdded, error) {
|
||||
ret := &edge_ctrl_pb.ApiSessionAdded{
|
||||
IsFullState: true,
|
||||
}
|
||||
|
||||
sessions, err := b.ae.GetHandlers().ApiSession.HandleQuery("true limit none")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
for _, session := range sessions.ApiSessions {
|
||||
sessionProto, err := b.modelApiSessionToProto(session.Token, session.IdentityId)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
ret.ApiSessions = append(ret.ApiSessions, sessionProto)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (b *Broker) getCurrentStateNetworkSessions(clusterId string) (*edge_ctrl_pb.SessionAdded, error) {
|
||||
ret := &edge_ctrl_pb.SessionAdded{
|
||||
IsFullState: true,
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`anyOf(service.clusters.id) = "%v" limit none`, clusterId)
|
||||
result, err := b.ae.Handlers.Session.HandleQuery(query)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
for _, session := range result.Sessions {
|
||||
nu, err := b.modelSessionToProto(session)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
ret.Sessions = append(ret.Sessions, nu)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (b *Broker) getActiveFingerprints(sessionId string) ([]string, error) {
|
||||
certs, err := b.ae.Handlers.Session.HandleReadSessionCerts(sessionId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ret []string
|
||||
|
||||
now := time.Now()
|
||||
for _, c := range certs {
|
||||
if now.After(c.ValidFrom) && now.Before(c.ValidTo) {
|
||||
ret = append(ret, c.Fingerprint)
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (b *Broker) getApiSessionFingerprints(identityId string) ([]string, error) {
|
||||
fingerprintsMap := map[string]bool{}
|
||||
|
||||
err := b.ae.Handlers.Identity.HandleCollectAuthenticators(identityId, func(entity model.BaseModelEntity) error {
|
||||
authenticator, ok := entity.(*model.Authenticator)
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %v when converting base model entity to authenticator", reflect.TypeOf(entity))
|
||||
}
|
||||
|
||||
for _, authPrint := range authenticator.Fingerprints() {
|
||||
fingerprintsMap[authPrint] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fingerprints []string
|
||||
for fingerprint, _ := range fingerprintsMap {
|
||||
fingerprints = append(fingerprints, fingerprint)
|
||||
}
|
||||
|
||||
return fingerprints, nil
|
||||
}
|
||||
|
||||
func (b *Broker) modelApiSessionToProto(token, identityId string) (*edge_ctrl_pb.ApiSession, error) {
|
||||
fingerprints, err := b.getApiSessionFingerprints(identityId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &edge_ctrl_pb.ApiSession{
|
||||
Token: token,
|
||||
CertFingerprints: fingerprints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Broker) modelSessionToProto(ns *model.Session) (*edge_ctrl_pb.Session, error) {
|
||||
service, err := b.ae.Handlers.Service.HandleRead(ns.ServiceId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert to session proto, could not find service: %s", err)
|
||||
}
|
||||
|
||||
apiSession, err := b.ae.Handlers.ApiSession.HandleRead(ns.ApiSessionId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert to network session proto, could not find session: %s", err)
|
||||
}
|
||||
|
||||
fps, err := b.getActiveFingerprints(ns.Id)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get fingerprints for network session: %s", err)
|
||||
}
|
||||
|
||||
svc, err := b.modelServiceToProto(service)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not convert service [%s] to proto: %s", service.Id, err)
|
||||
}
|
||||
|
||||
return &edge_ctrl_pb.Session{
|
||||
Token: ns.Token,
|
||||
SessionToken: apiSession.Token,
|
||||
Service: svc,
|
||||
CertFingerprints: fps,
|
||||
Hosting: ns.IsHosting,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *Broker) GetOnlineEdgeRouter(id string) *model.EdgeRouter {
|
||||
var edgeRouter *model.EdgeRouter
|
||||
|
||||
b.clusterMap.RangeEdgeRouterEntries(func(edgeRouterEntry *edgeRouterEntry) bool {
|
||||
if edgeRouterEntry.EdgeRouter.GetId() == id {
|
||||
edgeRouter = edgeRouterEntry.EdgeRouter
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return edgeRouter
|
||||
}
|
||||
|
||||
func (b *Broker) ClusterHasEdgeRouterOnline(clusterId string) bool {
|
||||
entries := b.clusterMap.GetOnlineEntries(clusterId)
|
||||
return len(entries) > 0
|
||||
}
|
||||
|
||||
func (b *Broker) GetOnlineEdgeRoutersByCluster(clusterId string) []*model.EdgeRouter {
|
||||
var ret []*model.EdgeRouter
|
||||
|
||||
for _, entry := range b.clusterMap.GetOnlineEntries(clusterId) {
|
||||
ret = append(ret, entry.EdgeRouter)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *Broker) RouterConnected(r *network.Router) {
|
||||
go func() {
|
||||
fp := formatFingerprint(r.Fingerprint)
|
||||
edgeRouter, _ := b.ae.Handlers.EdgeRouter.HandleReadOneByFingerprint(fp)
|
||||
|
||||
// not an edge router
|
||||
if edgeRouter == nil || edgeRouter.Id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
b.sendHello(r, edgeRouter, fp)
|
||||
}()
|
||||
|
||||
}
|
||||
|
||||
func (b *Broker) sendHello(r *network.Router, edgeRouter *model.EdgeRouter, fingerprint string) {
|
||||
serverHello := &edge_ctrl_pb.ServerHello{
|
||||
Version: version.GetVersion(),
|
||||
}
|
||||
|
||||
if buf, err := proto.Marshal(serverHello); err == nil {
|
||||
if waitCh, err := r.Control.SendAndWait(channel2.NewMessage(ServerHelloType, buf)); err == nil {
|
||||
|
||||
select {
|
||||
case resp := <-waitCh:
|
||||
if resp.ContentType == ClientHelloType {
|
||||
respHello := &edge_ctrl_pb.ClientHello{}
|
||||
if err := proto.Unmarshal(resp.Body, respHello); err == nil {
|
||||
pfxlog.Logger().
|
||||
WithField("version", respHello.Version).
|
||||
WithField("hostname", respHello.Hostname).
|
||||
WithField("protocols", respHello.Protocols).
|
||||
WithField("data", respHello.Data).
|
||||
Info("edge router responded with client hello")
|
||||
|
||||
protocols := map[string]string{}
|
||||
for _, p := range respHello.Protocols {
|
||||
ingressUrl := fmt.Sprintf("%s://%s", p, respHello.Hostname)
|
||||
protocols[p] = ingressUrl
|
||||
}
|
||||
|
||||
//in memory only
|
||||
edgeRouter.Hostname = &respHello.Hostname
|
||||
edgeRouter.EdgeRouterProtocols = protocols
|
||||
|
||||
//todo: restrict version?
|
||||
pfxlog.Logger().Infof("edge router connecting with version [%s] to controller with version [%s]", respHello.Version, version.GetVersion())
|
||||
|
||||
b.AddEdgeRouter(r.Control, edgeRouter)
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("could not unmarshal clientHello after serverHello")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if resp.ContentType == ErrorType {
|
||||
respErr := &edge_ctrl_pb.Error{}
|
||||
if err := proto.Unmarshal(resp.Body, respErr); err == nil {
|
||||
pfxlog.Logger().WithField("cause", respErr.Cause).WithField("code", respErr.Code).WithField("message", respErr.Message).
|
||||
Error("client responded with error after serverHello")
|
||||
return
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("could not unmarshal error from client after serverHello")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case <-time.After(5 * time.Second):
|
||||
pfxlog.Logger().Error("timeout - waiting for clientHello from edge router")
|
||||
}
|
||||
|
||||
} else {
|
||||
pfxlog.Logger().WithField("cause", err).Error("could not send serverHello message for edge router")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Broker) RouterDisconnected(r *network.Router) {
|
||||
go func() {
|
||||
fp := formatFingerprint(r.Fingerprint)
|
||||
edgeRouter, _ := b.ae.Handlers.EdgeRouter.HandleReadOneByFingerprint(fp)
|
||||
|
||||
// not an edge router
|
||||
if edgeRouter == nil || edgeRouter.Id == "" {
|
||||
return
|
||||
}
|
||||
|
||||
b.clusterMap.RemoveEntry(edgeRouter.ClusterId, edgeRouter.Id)
|
||||
}()
|
||||
}
|
||||
|
||||
func formatFingerprint(fp string) string {
|
||||
fp = strings.ToUpper(fp)
|
||||
|
||||
return insertNth(fp, 2, ":")
|
||||
}
|
||||
|
||||
func insertNth(s string, n int, i string) string {
|
||||
rs := []rune(i)
|
||||
var buffer bytes.Buffer
|
||||
var n_1 = n - 1
|
||||
var l_1 = len(s) - 1
|
||||
for i, cr := range s {
|
||||
buffer.WriteRune(cr)
|
||||
if i%n == n_1 && i != l_1 {
|
||||
for _, r := range rs {
|
||||
buffer.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
Vendored
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
)
|
||||
|
||||
type DefaultEventLogger struct {
|
||||
Ae *AppEnv
|
||||
}
|
||||
|
||||
func (el *DefaultEventLogger) Log(actorType, actorId, eventType, entityType, entityId, formatString string, formatData []string, data map[interface{}]interface{}) {
|
||||
fmtDataStr, err := json.Marshal(formatData)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("could not save event log")
|
||||
return
|
||||
}
|
||||
|
||||
var fmtInt []interface{}
|
||||
|
||||
for _, fd := range formatData {
|
||||
fmtInt = append(fmtInt, fd)
|
||||
}
|
||||
|
||||
pm := migration.PropertyMap{}
|
||||
|
||||
for k, v := range data {
|
||||
ks := k.(string)
|
||||
pm[ks] = v
|
||||
}
|
||||
|
||||
_, err = el.Ae.Handlers.EventLog.HandleCreate(&model.EventLog{
|
||||
Type: eventType,
|
||||
ActorId: actorId,
|
||||
ActorType: actorType,
|
||||
EntityId: entityId,
|
||||
EntityType: entityType,
|
||||
FormatData: string(fmtDataStr),
|
||||
FormatString: formatString,
|
||||
FormattedMessage: fmt.Sprintf(formatString, fmtInt...),
|
||||
Data: pm,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Errorf("could not store event log: %s", err)
|
||||
}
|
||||
}
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"time"
|
||||
)
|
||||
|
||||
type BaseApi struct {
|
||||
Id string `json:"id"`
|
||||
CreatedAt *time.Time `json:"createdAt"`
|
||||
UpdatedAt *time.Time `json:"updatedAt"`
|
||||
Links *response.Links `json:"_links"`
|
||||
Tags map[string]interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
func FromBaseDbEntity(entity *migration.BaseDbEntity) *BaseApi {
|
||||
var tags map[string]interface{}
|
||||
if entity.Tags != nil {
|
||||
tags = *entity.Tags
|
||||
}
|
||||
return &BaseApi{
|
||||
Id: entity.ID,
|
||||
UpdatedAt: entity.UpdatedAt,
|
||||
CreatedAt: entity.CreatedAt,
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
func FromBaseModelEntity(entity model.BaseModelEntity) *BaseApi {
|
||||
createdAt := entity.GetCreatedAt()
|
||||
updatedAt := entity.GetUpdatedAt()
|
||||
return &BaseApi{
|
||||
Id: entity.GetId(),
|
||||
UpdatedAt: &updatedAt,
|
||||
CreatedAt: &createdAt,
|
||||
Tags: entity.GetTags(),
|
||||
}
|
||||
}
|
||||
Vendored
+184
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/config"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/packrsource"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration/certOLD"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration/updbOLD"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/golang-migrate/migrate"
|
||||
"github.com/golang-migrate/migrate/database"
|
||||
"github.com/golang-migrate/migrate/database/postgres"
|
||||
"github.com/golang-migrate/migrate/source"
|
||||
_ "github.com/golang-migrate/migrate/source/file"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const PackrPrefix = "embedded"
|
||||
const MigrationPathPostgres = "embedded://db/postgres/migrations"
|
||||
|
||||
type DriverGenerator interface {
|
||||
AppDriver(cs string) (*gorm.DB, error)
|
||||
MigrationDriver(cs string) (database.Driver, error)
|
||||
Name() string
|
||||
}
|
||||
|
||||
type PostgresDriverGenerator struct {
|
||||
}
|
||||
|
||||
func (PostgresDriverGenerator) Name() string {
|
||||
return "postgres"
|
||||
}
|
||||
|
||||
func (pg *PostgresDriverGenerator) AppDriver(cs string) (*gorm.DB, error) {
|
||||
appCs, err := pg.appString(cs)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gorm.Open("postgres", appCs)
|
||||
}
|
||||
|
||||
func (pg *PostgresDriverGenerator) MigrationDriver(cs string) (database.Driver, error) {
|
||||
migCs := pg.migrationString(cs)
|
||||
|
||||
s, err := sql.Open("postgres", migCs)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Panic("error during sql.Open")
|
||||
}
|
||||
return postgres.WithInstance(s, &postgres.Config{
|
||||
MigrationsTable: "ziti_edge_version",
|
||||
})
|
||||
}
|
||||
|
||||
func (pg *PostgresDriverGenerator) appString(s string) (string, error) {
|
||||
u, err := url.Parse(s)
|
||||
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("connection string must be a well formatted URL: %s", err)
|
||||
}
|
||||
|
||||
q := u.Query()
|
||||
q.Set("search_path", "ziti_edge,public")
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
func (pg *PostgresDriverGenerator) migrationString(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func InitPersistence(p *config.Persistence) *gorm.DB {
|
||||
if p == nil || p.Postgres.ConnectionUrl == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, migration := range certOLD.GetCertMigrations() {
|
||||
p.AddMigrationBox(migration)
|
||||
}
|
||||
|
||||
for _, migration := range updbOLD.GetUpdbMigrations() {
|
||||
p.AddMigrationBox(migration)
|
||||
}
|
||||
|
||||
source.Register(PackrPrefix, packrsource.NewPakrSource(p.GetBoxes()...))
|
||||
|
||||
log := pfxlog.Logger()
|
||||
|
||||
var dg DriverGenerator
|
||||
var cs string
|
||||
|
||||
switch {
|
||||
case p.Postgres != nil:
|
||||
dg = &PostgresDriverGenerator{}
|
||||
cs = p.Postgres.ConnectionUrl
|
||||
default:
|
||||
log.Fatal("no supported persistence configuration defined")
|
||||
}
|
||||
|
||||
md, err := dg.MigrationDriver(cs)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect to data store (%s) to check schema version: %+v", dg.Name(), err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := md.Close()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
ad, err := dg.AppDriver(cs)
|
||||
|
||||
ad.LogMode(true)
|
||||
ad.SetLogger(&logrusLogger{})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
migrateDb(p, md)
|
||||
|
||||
return ad
|
||||
}
|
||||
|
||||
func migrateDb(p *config.Persistence, drv database.Driver) {
|
||||
log := pfxlog.Logger()
|
||||
|
||||
m, err := migrate.NewWithDatabaseInstance(MigrationPathPostgres, p.Postgres.DbName, drv)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("could not configure migration client: ", err)
|
||||
}
|
||||
|
||||
log.Info("checking database schema version...")
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
log.Fatalf("Failure while migrating database: %+v", err)
|
||||
}
|
||||
}()
|
||||
err = m.Up()
|
||||
|
||||
if err != nil {
|
||||
if err != migrate.ErrNoChange {
|
||||
log.Fatal("...could not migrate database: ", err)
|
||||
}
|
||||
log.Info("...no database upgrade required")
|
||||
} else {
|
||||
log.Info("...database upgraded")
|
||||
}
|
||||
}
|
||||
|
||||
type logrusLogger struct{}
|
||||
|
||||
func (logrusLogger) Print(v ...interface{}) {
|
||||
pfxlog.Logger().WithField("type", "sql").Trace(v...)
|
||||
}
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package env
|
||||
|
||||
var routerFuncs []AddRouterFunc
|
||||
|
||||
type AddRouterFunc func(ae *AppEnv)
|
||||
|
||||
type ApiRouter interface {
|
||||
Register(ae *AppEnv)
|
||||
}
|
||||
|
||||
func AddRouter(rf ApiRouter) {
|
||||
routerFuncs = append(routerFuncs, rf.Register)
|
||||
}
|
||||
|
||||
func GetRouters() []AddRouterFunc {
|
||||
return routerFuncs
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package handler_edge_ctrl
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-foundation/channel2"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/pb/edge_ctrl_pb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
)
|
||||
|
||||
type sessionHeartbeatHandler struct {
|
||||
appEnv *env.AppEnv
|
||||
}
|
||||
|
||||
func NewSessionHeartbeatHandler(appEnv *env.AppEnv) *sessionHeartbeatHandler {
|
||||
return &sessionHeartbeatHandler{
|
||||
appEnv: appEnv,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *sessionHeartbeatHandler) ContentType() int32 {
|
||||
return env.ApiSessionHeartbeatType
|
||||
}
|
||||
|
||||
func (h *sessionHeartbeatHandler) HandleReceive(msg *channel2.Message, ch channel2.Channel) {
|
||||
go func() {
|
||||
req := &edge_ctrl_pb.ApiSessionHeartbeat{}
|
||||
if err := proto.Unmarshal(msg.Body, req); err == nil {
|
||||
|
||||
err := h.appEnv.Stores.Session.MarkActivity(req.Tokens)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().
|
||||
WithError(err).
|
||||
Error("unable to set activity for heartbeat")
|
||||
}
|
||||
|
||||
} else {
|
||||
pfxlog.Logger().Panic("could not convert message as session heartbeat")
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
type AlwaysAllow struct{}
|
||||
|
||||
var always = &AlwaysAllow{}
|
||||
|
||||
func (a *AlwaysAllow) IsAllowed(_ ...string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Always() *AlwaysAllow {
|
||||
return always
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
type RequireAdmin struct{}
|
||||
|
||||
var requireAdmin = &RequireAdmin{}
|
||||
|
||||
func IsAdmin() *RequireAdmin {
|
||||
return requireAdmin
|
||||
}
|
||||
|
||||
func (ia *RequireAdmin) IsAllowed(identityPerms ...string) bool {
|
||||
for _, p := range identityPerms {
|
||||
if p == AdminPermission {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
type RequireAuthenticated struct{}
|
||||
|
||||
var isAuthenticated = &RequireAuthenticated{}
|
||||
|
||||
func IsAuthenticated() *RequireAuthenticated {
|
||||
return isAuthenticated
|
||||
}
|
||||
|
||||
func (ir *RequireAuthenticated) IsAllowed(identityPerms ...string) bool {
|
||||
for _, p := range identityPerms {
|
||||
if p == AuthenticatedPermission {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
const (
|
||||
AdminPermission = "ADMIN"
|
||||
AuthenticatedPermission = "AUTHENTICATED"
|
||||
)
|
||||
|
||||
type Resolver interface {
|
||||
IsAllowed(...string) bool
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
type RequireAll struct {
|
||||
required []string
|
||||
}
|
||||
|
||||
func NewRequireAll(perms ...string) *RequireAll {
|
||||
ra := &RequireAll{
|
||||
required: perms,
|
||||
}
|
||||
return ra
|
||||
}
|
||||
|
||||
func (ra *RequireAll) IsAllowed(identityPerms ...string) bool {
|
||||
ps := map[string]bool{}
|
||||
|
||||
if len(identityPerms) < len(ra.required) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, p := range identityPerms {
|
||||
ps[p] = true
|
||||
|
||||
//short cut admins
|
||||
if p == AdminPermission {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range ra.required {
|
||||
if _, ok := ps[r]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package permissions
|
||||
|
||||
type RequireOne struct {
|
||||
required map[string]bool
|
||||
}
|
||||
|
||||
func NewRequireOne(perms ...string) *RequireOne {
|
||||
ro := &RequireOne{}
|
||||
ro.required = map[string]bool{}
|
||||
|
||||
for _, p := range perms {
|
||||
ro.required[p] = true
|
||||
}
|
||||
|
||||
return ro
|
||||
}
|
||||
|
||||
func (ro *RequireOne) IsAllowed(identityPerms ...string) bool {
|
||||
for _, p := range identityPerms {
|
||||
|
||||
//short cut admins
|
||||
if p == AdminPermission {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := ro.required[p]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/persistence"
|
||||
"github.com/netfoundry/ziti-edge/edge/runner"
|
||||
"fmt"
|
||||
"go.etcd.io/bbolt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AppWanEnforcer struct {
|
||||
appEnv *env.AppEnv
|
||||
*runner.BaseOperation
|
||||
}
|
||||
|
||||
func NewAppWanEnforcer(appEnv *env.AppEnv, f time.Duration) *AppWanEnforcer {
|
||||
return &AppWanEnforcer{
|
||||
appEnv: appEnv,
|
||||
BaseOperation: runner.NewBaseOperation("AppWanEnforcer", f)}
|
||||
}
|
||||
|
||||
func (enforcer *AppWanEnforcer) Run() error {
|
||||
result, err := enforcer.appEnv.GetHandlers().Session.HandleQuery("")
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service := &persistence.EdgeService{}
|
||||
|
||||
var sessionsToRemove []string
|
||||
err = enforcer.appEnv.GetDbProvider().GetDb().View(func(tx *bbolt.Tx) error {
|
||||
for _, session := range result.Sessions {
|
||||
apiSession := &persistence.ApiSession{}
|
||||
_, err := enforcer.appEnv.GetStores().ApiSession.BaseLoadOneById(tx, session.ApiSessionId, apiSession)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
identity := &persistence.Identity{}
|
||||
_, err = enforcer.appEnv.GetStores().Identity.BaseLoadOneById(tx, apiSession.IdentityId, identity)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if identity.IsAdmin == true {
|
||||
continue
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`id = "%v" and anyOf(appwans.identities.id) = "%v"`, session.ServiceId, apiSession.IdentityId)
|
||||
found, err := enforcer.appEnv.GetStores().EdgeService.BaseLoadOneByQuery(tx, query, service)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
sessionsToRemove = append(sessionsToRemove, session.Id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, sessionId := range sessionsToRemove {
|
||||
_ = enforcer.appEnv.GetHandlers().Session.HandleDelete(sessionId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/runner"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SessionEnforcer struct {
|
||||
appEnv *env.AppEnv
|
||||
sessionTimeout time.Duration
|
||||
*runner.BaseOperation
|
||||
}
|
||||
|
||||
func NewSessionEnforcer(appEnv *env.AppEnv, frequency time.Duration, sessionTimeout time.Duration) *SessionEnforcer {
|
||||
if sessionTimeout < 60*time.Second {
|
||||
pfxlog.Logger().Panic("sessionTimeout can not be less than 60 seconds")
|
||||
}
|
||||
|
||||
pfxlog.Logger().
|
||||
WithField("sessionTimeout", sessionTimeout.String()).
|
||||
WithField("frequency", frequency.String()).
|
||||
Info("session enforcer configured")
|
||||
|
||||
return &SessionEnforcer{
|
||||
appEnv: appEnv,
|
||||
sessionTimeout: sessionTimeout,
|
||||
BaseOperation: runner.NewBaseOperation("SessionEnforcer", frequency),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SessionEnforcer) Run() error {
|
||||
oldest := time.Now().Add(s.sessionTimeout * -1)
|
||||
query := fmt.Sprintf("updatedAt < datetime(%s)", oldest.UTC().Format(time.RFC3339))
|
||||
|
||||
result, err := s.appEnv.GetHandlers().ApiSession.HandleQuery(query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, apiSession := range result.ApiSessions {
|
||||
_ = s.appEnv.GetHandlers().ApiSession.HandleDelete(apiSession.Id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
)
|
||||
|
||||
const EntityNameSession = "sessions"
|
||||
|
||||
func NewApiSessionEntityRef(s *model.ApiSession) *EntityApiRef {
|
||||
links := &response.Links{
|
||||
"self": NewApiSessionLink(s.Id),
|
||||
}
|
||||
|
||||
return &EntityApiRef{
|
||||
Id: s.Id,
|
||||
Name: nil,
|
||||
Links: links,
|
||||
}
|
||||
}
|
||||
|
||||
func NewApiSessionLink(sessionId string) *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s", EntityNameSession, sessionId))
|
||||
}
|
||||
|
||||
type ApiSessionApiList struct {
|
||||
*env.BaseApi
|
||||
Token *string `json:"token"`
|
||||
Identity *EntityApiRef `json:"identity"`
|
||||
}
|
||||
|
||||
func (ApiSessionApiList) BuildSelfLink(id string) *response.Link {
|
||||
return NewApiSessionLink(id)
|
||||
}
|
||||
|
||||
func (e *ApiSessionApiList) GetSelfLink() *response.Link {
|
||||
return e.BuildSelfLink(e.Id)
|
||||
}
|
||||
|
||||
func (e *ApiSessionApiList) PopulateLinks() {
|
||||
if e.Links == nil {
|
||||
e.Links = &response.Links{
|
||||
EntityNameSelf: e.GetSelfLink(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *ApiSessionApiList) ToEntityApiRef() *EntityApiRef {
|
||||
e.PopulateLinks()
|
||||
return &EntityApiRef{
|
||||
Entity: EntityNameSession,
|
||||
Name: nil,
|
||||
Id: e.Id,
|
||||
Links: e.Links,
|
||||
}
|
||||
}
|
||||
|
||||
func MapApiSessionToApiEntity(_ *env.AppEnv, _ *response.RequestContext, e model.BaseModelEntity) (BaseApiEntity, error) {
|
||||
i, ok := e.(*model.ApiSession)
|
||||
|
||||
if !ok {
|
||||
err := fmt.Errorf("entity is not an ApiSession \"%s\"", e.GetId())
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
al, err := MapApiSessionToApiList(i)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("could not convert to API entity \"%s\": %s", e.GetId(), err)
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return al, nil
|
||||
}
|
||||
|
||||
func MapApiSessionToApiList(i *model.ApiSession) (*ApiSessionApiList, error) {
|
||||
ret := &ApiSessionApiList{
|
||||
BaseApi: env.FromBaseModelEntity(i),
|
||||
Token: &i.Token,
|
||||
Identity: NewIdentityEntityRef(i.Identity),
|
||||
}
|
||||
|
||||
ret.PopulateLinks()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := NewApiSessionRouter()
|
||||
env.AddRouter(r)
|
||||
}
|
||||
|
||||
type ApiSessionRouter struct {
|
||||
BasePath string
|
||||
IdType response.IdType
|
||||
}
|
||||
|
||||
func NewApiSessionRouter() *ApiSessionRouter {
|
||||
return &ApiSessionRouter{
|
||||
BasePath: "/" + EntityNameSession,
|
||||
IdType: response.IdTypeUuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ir *ApiSessionRouter) Register(ae *env.AppEnv) {
|
||||
registerReadDeleteOnlyRouter(ae, ae.RootRouter, ir.BasePath, ir, permissions.IsAdmin())
|
||||
}
|
||||
|
||||
func (ir *ApiSessionRouter) List(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListWithHandler(ae, rc, ae.Handlers.ApiSession, MapApiSessionToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *ApiSessionRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DetailWithHandler(ae, rc, ae.Handlers.ApiSession, MapApiSessionToApiEntity, ir.IdType)
|
||||
}
|
||||
|
||||
func (ir *ApiSessionRouter) Delete(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DeleteWithHandler(rc, ir.IdType, ae.Handlers.ApiSession)
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/util/stringz"
|
||||
)
|
||||
|
||||
const EntityNameAppWan = "app-wans"
|
||||
|
||||
type AppWanApiUpdate struct {
|
||||
Tags *map[string]interface{} `json:"tags"`
|
||||
Name *string `json:"name"`
|
||||
}
|
||||
|
||||
func (i *AppWanApiUpdate) ToModel(id string) *model.Appwan {
|
||||
result := &model.Appwan{}
|
||||
result.Id = id
|
||||
result.Name = stringz.OrEmpty(i.Name)
|
||||
if i.Tags != nil {
|
||||
result.Tags = *i.Tags
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type AppWanApiCreate struct {
|
||||
Name *string `json:"name"`
|
||||
Identities []string `json:"identities"`
|
||||
Services []string `json:"services"`
|
||||
Tags *map[string]interface{} `json:"tags"`
|
||||
}
|
||||
|
||||
func NewAppWanApiCreate() *AppWanApiCreate {
|
||||
return &AppWanApiCreate{
|
||||
Identities: []string{},
|
||||
Services: []string{},
|
||||
}
|
||||
}
|
||||
|
||||
func (i *AppWanApiCreate) ToModel() *model.Appwan {
|
||||
result := &model.Appwan{}
|
||||
result.Name = stringz.OrEmpty(i.Name)
|
||||
if i.Tags != nil {
|
||||
result.Tags = *i.Tags
|
||||
}
|
||||
result.Identities = i.Identities
|
||||
result.Services = i.Services
|
||||
return result
|
||||
}
|
||||
|
||||
type AppWanApiList struct {
|
||||
*env.BaseApi
|
||||
Name *string `json:"name"`
|
||||
}
|
||||
|
||||
func (AppWanApiList) BuildSelfLink(id string) *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s", EntityNameAppWan, id))
|
||||
}
|
||||
|
||||
func (e *AppWanApiList) GetSelfLink() *response.Link {
|
||||
return e.BuildSelfLink(e.Id)
|
||||
}
|
||||
|
||||
func (e *AppWanApiList) GetIdentitiesLink() *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s/%s", EntityNameAppWan, e.Id, EntityNameIdentity))
|
||||
}
|
||||
|
||||
func (e *AppWanApiList) GetServicesLink() *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s/%s", EntityNameAppWan, e.Id, EntityNameService))
|
||||
}
|
||||
|
||||
func (e *AppWanApiList) PopulateLinks() {
|
||||
if e.Links == nil {
|
||||
e.Links = &response.Links{
|
||||
EntityNameSelf: e.GetSelfLink(),
|
||||
EntityNameService: e.GetServicesLink(),
|
||||
EntityNameIdentity: e.GetIdentitiesLink(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *AppWanApiList) ToEntityApiRef() *EntityApiRef {
|
||||
e.PopulateLinks()
|
||||
return &EntityApiRef{
|
||||
Entity: EntityNameAppWan,
|
||||
Name: e.Name,
|
||||
Id: e.Id,
|
||||
Links: e.Links,
|
||||
}
|
||||
}
|
||||
|
||||
func MapAppWanToApiEntity(_ *env.AppEnv, _ *response.RequestContext, e model.BaseModelEntity) (BaseApiEntity, error) {
|
||||
i, ok := e.(*model.Appwan)
|
||||
|
||||
if !ok {
|
||||
err := fmt.Errorf("entity is not an appwan \"%s\"", e.GetId())
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
al, err := MapAppWanToApiList(i)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("could not convert to API entity \"%s\": %s", e.GetId(), err)
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return al, nil
|
||||
}
|
||||
|
||||
func MapAppWanToApiList(i *model.Appwan) (*AppWanApiList, error) {
|
||||
ret := &AppWanApiList{
|
||||
BaseApi: env.FromBaseModelEntity(i),
|
||||
Name: &i.Name,
|
||||
}
|
||||
|
||||
ret.PopulateLinks()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/persistence"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := NewAppWanRouter()
|
||||
env.AddRouter(r)
|
||||
}
|
||||
|
||||
type AppWanRouter struct {
|
||||
BasePath string
|
||||
IdType response.IdType
|
||||
}
|
||||
|
||||
func NewAppWanRouter() *AppWanRouter {
|
||||
return &AppWanRouter{
|
||||
BasePath: "/" + EntityNameAppWan,
|
||||
IdType: response.IdTypeUuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Register(ae *env.AppEnv) {
|
||||
sr := registerCrudRouter(ae, ae.RootRouter, ir.BasePath, ir, permissions.IsAdmin())
|
||||
|
||||
serviceUrlWithSlash := fmt.Sprintf("/{%s}/services", response.IdPropertyName)
|
||||
serviceUrlWithOutSlash := fmt.Sprintf("/{%s}/services/", response.IdPropertyName)
|
||||
|
||||
servicesListHandler := ae.WrapHandler(ir.ListServices, permissions.IsAdmin())
|
||||
servicesAddHandler := ae.WrapHandler(ir.AddServices, permissions.IsAdmin())
|
||||
servicesRemoveHandler := ae.WrapHandler(ir.RemoveServicesBulk, permissions.IsAdmin())
|
||||
servicesSetHandler := ae.WrapHandler(ir.SetServices, permissions.IsAdmin())
|
||||
|
||||
sr.HandleFunc(serviceUrlWithSlash, servicesListHandler).Methods(http.MethodGet)
|
||||
sr.HandleFunc(serviceUrlWithOutSlash, servicesListHandler).Methods(http.MethodGet)
|
||||
|
||||
sr.HandleFunc(serviceUrlWithSlash, servicesAddHandler).Methods(http.MethodPut)
|
||||
sr.HandleFunc(serviceUrlWithOutSlash, servicesAddHandler).Methods(http.MethodPut)
|
||||
|
||||
sr.HandleFunc(serviceUrlWithSlash, servicesRemoveHandler).Methods(http.MethodDelete)
|
||||
sr.HandleFunc(serviceUrlWithOutSlash, servicesRemoveHandler).Methods(http.MethodDelete)
|
||||
|
||||
sr.HandleFunc(serviceUrlWithSlash, servicesSetHandler).Methods(http.MethodPost)
|
||||
sr.HandleFunc(serviceUrlWithOutSlash, servicesSetHandler).Methods(http.MethodPost)
|
||||
|
||||
identityUrlWithSlash := fmt.Sprintf("/{%s}/identities", response.IdPropertyName)
|
||||
identityUrlWithOutSlash := fmt.Sprintf("/{%s}/identities/", response.IdPropertyName)
|
||||
|
||||
identitiesListHandler := ae.WrapHandler(ir.ListIdentities, permissions.IsAdmin())
|
||||
identitiesAddHandler := ae.WrapHandler(ir.AddIdentities, permissions.IsAdmin())
|
||||
identitiesBulkRemoveHandler := ae.WrapHandler(ir.RemoveIdentitiesBulk, permissions.IsAdmin())
|
||||
identitiesSetHandler := ae.WrapHandler(ir.SetIdentities, permissions.IsAdmin())
|
||||
|
||||
sr.HandleFunc(identityUrlWithSlash, identitiesListHandler).Methods(http.MethodGet)
|
||||
sr.HandleFunc(identityUrlWithOutSlash, identitiesListHandler).Methods(http.MethodGet)
|
||||
|
||||
sr.HandleFunc(identityUrlWithSlash, identitiesAddHandler).Methods(http.MethodPut)
|
||||
sr.HandleFunc(identityUrlWithOutSlash, identitiesAddHandler).Methods(http.MethodPut)
|
||||
|
||||
sr.HandleFunc(identityUrlWithSlash, identitiesBulkRemoveHandler).Methods(http.MethodDelete)
|
||||
sr.HandleFunc(identityUrlWithOutSlash, identitiesBulkRemoveHandler).Methods(http.MethodDelete)
|
||||
|
||||
sr.HandleFunc(identityUrlWithSlash, identitiesSetHandler).Methods(http.MethodPost)
|
||||
sr.HandleFunc(identityUrlWithOutSlash, identitiesSetHandler).Methods(http.MethodPost)
|
||||
|
||||
identityUrlWithSlashWithSubId := fmt.Sprintf("/{%s}/identities/{%s}", response.IdPropertyName, response.SubIdPropertyName)
|
||||
identityUrlWithOutSlashWithSubId := fmt.Sprintf("/{%s}/identities/{%s}/", response.IdPropertyName, response.SubIdPropertyName)
|
||||
|
||||
identityRemoveHandler := ae.WrapHandler(ir.RemoveIdentity, permissions.IsAdmin())
|
||||
|
||||
sr.HandleFunc(identityUrlWithSlashWithSubId, identityRemoveHandler).Methods(http.MethodDelete)
|
||||
sr.HandleFunc(identityUrlWithOutSlashWithSubId, identityRemoveHandler).Methods(http.MethodDelete)
|
||||
|
||||
serviceUrlWithSlashWithSubId := fmt.Sprintf("/{%s}/services/{%s}", response.IdPropertyName, response.SubIdPropertyName)
|
||||
serviceUrlWithOutSlashWithSubId := fmt.Sprintf("/{%s}/services/{%s}/", response.IdPropertyName, response.SubIdPropertyName)
|
||||
|
||||
serviceRemoveHandler := ae.WrapHandler(ir.RemoveService, permissions.IsAdmin())
|
||||
|
||||
sr.HandleFunc(serviceUrlWithSlashWithSubId, serviceRemoveHandler).Methods(http.MethodDelete)
|
||||
sr.HandleFunc(serviceUrlWithOutSlashWithSubId, serviceRemoveHandler).Methods(http.MethodDelete)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) List(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListWithHandler(ae, rc, ae.Handlers.Appwan, MapAppWanToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DetailWithHandler(ae, rc, ae.Handlers.Appwan, MapAppWanToApiEntity, ir.IdType)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Create(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := NewAppWanApiCreate()
|
||||
Create(rc, rc.RequestResponder, ae.Schemes.AppWan.Post, apiEntity, (&AppWanApiList{}).BuildSelfLink, func() (string, error) {
|
||||
return ae.Handlers.Appwan.HandleCreate(apiEntity.ToModel())
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Delete(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DeleteWithHandler(rc, ir.IdType, ae.Handlers.Appwan)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Update(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &AppWanApiUpdate{}
|
||||
Update(rc, ae.Schemes.AppWan.Put, ir.IdType, apiEntity, func(id string) error {
|
||||
return ae.Handlers.Appwan.HandleUpdate(apiEntity.ToModel(id))
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) Patch(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &AppWanApiUpdate{}
|
||||
Patch(rc, ae.Schemes.AppWan.Patch, ir.IdType, apiEntity, func(id string, fields JsonFields) error {
|
||||
return ae.Handlers.Appwan.HandlePatch(apiEntity.ToModel(id), fields.ConcatNestedNames())
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) ListServices(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListAssociations(ae, rc, ir.IdType, ae.Handlers.Appwan.HandleCollectServices, MapServiceToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) AddServices(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.GetStores().Appwan, model.AssociationsActionAdd, persistence.FieldAppwanServices)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) RemoveServicesBulk(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.GetStores().Appwan, model.AssociationsActionRemove, persistence.FieldAppwanServices)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) RemoveService(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
RemoveAssociationFor(ae, rc, ir.IdType, ae.GetStores().Appwan, persistence.FieldAppwanServices)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) SetServices(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.GetStores().Appwan, model.AssociationsActionSet, persistence.FieldAppwanServices)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) ListIdentities(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListAssociations(ae, rc, ir.IdType, ae.Handlers.Appwan.HandleCollectIdentities, MapIdentityToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) AddIdentities(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.BoltStores.Appwan, model.AssociationsActionAdd, persistence.FieldAppwanIdentities)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) RemoveIdentitiesBulk(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.BoltStores.Appwan, model.AssociationsActionRemove, persistence.FieldAppwanIdentities)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) RemoveIdentity(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
RemoveAssociationFor(ae, rc, ir.IdType, ae.BoltStores.Appwan, persistence.FieldAppwanIdentities)
|
||||
}
|
||||
|
||||
func (ir *AppWanRouter) SetIdentities(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociationsFor(ae, rc, ir.IdType, ae.BoltStores.Appwan, model.AssociationsActionSet, persistence.FieldAppwanIdentities)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/google/uuid"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := NewAuthRouter()
|
||||
env.AddRouter(r)
|
||||
}
|
||||
|
||||
type AuthRouter struct {
|
||||
}
|
||||
|
||||
func NewAuthRouter() *AuthRouter {
|
||||
return &AuthRouter{}
|
||||
}
|
||||
|
||||
func (ro *AuthRouter) Register(ae *env.AppEnv) {
|
||||
authHandler := ae.WrapHandler(ro.authHandler, permissions.Always())
|
||||
|
||||
ae.RootRouter.HandleFunc("/authenticate", authHandler).Methods("POST")
|
||||
ae.RootRouter.HandleFunc("/authenticate/", authHandler).Methods("POST")
|
||||
}
|
||||
|
||||
func (ro *AuthRouter) authHandler(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
authContext := &model.AuthContextHttp{}
|
||||
err := authContext.FillFromHttpRequest(rc.Request)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
identity, err := ae.Handlers.Authenticator.HandleIsAuthorized(authContext)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if identity == nil {
|
||||
rc.RequestResponder.RespondWithUnauthorizedError(rc)
|
||||
return
|
||||
}
|
||||
|
||||
token := uuid.New().String()
|
||||
|
||||
s := &model.ApiSession{
|
||||
IdentityId: identity.Id,
|
||||
Token: token,
|
||||
}
|
||||
|
||||
sessionId, err := ae.Handlers.ApiSession.HandleCreate(s)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := ae.Handlers.ApiSession.HandleRead(sessionId)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().WithField("cause", err).Error("loading session by id resulted in an error")
|
||||
rc.RequestResponder.RespondWithUnauthorizedError(rc)
|
||||
}
|
||||
|
||||
currentSession, err := RenderCurrentSessionApiListEntity(session, ae.Config.SessionTimeoutDuration())
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
expiration := time.Now().Add(365 * 24 * time.Hour)
|
||||
cookie := http.Cookie{Name: ae.AuthCookieName, Value: token, Expires: expiration}
|
||||
rc.ResponseWriter.Header().Set(ae.AuthHeaderName, session.Token)
|
||||
http.SetCookie(rc.ResponseWriter, &cookie)
|
||||
|
||||
rc.RequestResponder.RespondWithOk(currentSession, nil)
|
||||
}
|
||||
@@ -0,0 +1,602 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/apierror"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/persistence"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/util"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/xeipuuv/gojsonschema"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
EntityNameSelf = "self"
|
||||
)
|
||||
|
||||
type associationIdArrayRequest struct {
|
||||
Ids []string `json:"ids"`
|
||||
}
|
||||
|
||||
func unmarshal(body []byte, in interface{}) error {
|
||||
err := json.Unmarshal(body, in)
|
||||
|
||||
if err != nil {
|
||||
err = apierror.GetJsonParseError(err, body)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type JsonFields map[string]bool
|
||||
|
||||
func (j JsonFields) IsUpdated(key string) bool {
|
||||
_, ok := j[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (j JsonFields) ConcatNestedNames() JsonFields {
|
||||
for key, val := range j {
|
||||
if strings.Contains(key, ".") {
|
||||
delete(j, key)
|
||||
key = strings.ReplaceAll(key, ".", "")
|
||||
j[key] = val
|
||||
}
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func getFields(body []byte) (JsonFields, error) {
|
||||
jsonMap := map[string]interface{}{}
|
||||
err := json.Unmarshal(body, &jsonMap)
|
||||
|
||||
if err != nil {
|
||||
return nil, apierror.GetJsonParseError(err, body)
|
||||
}
|
||||
|
||||
resultMap := JsonFields{}
|
||||
getJsonFields("", jsonMap, resultMap)
|
||||
return resultMap, nil
|
||||
}
|
||||
|
||||
func getJsonFields(prefix string, m map[string]interface{}, result JsonFields) {
|
||||
for k, v := range m {
|
||||
name := strings.Title(k)
|
||||
if subMap, ok := v.(map[string]interface{}); ok {
|
||||
getJsonFields(prefix+name+".", subMap, result)
|
||||
} else {
|
||||
isSet := v != nil
|
||||
result[prefix+name] = isSet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func modelToApi(ae *env.AppEnv, rc *response.RequestContext, mapper ModelToApiMapper, es []model.BaseModelEntity) ([]BaseApiEntity, error) {
|
||||
apiEntities := make([]BaseApiEntity, 0)
|
||||
|
||||
for _, e := range es {
|
||||
al, err := mapper(ae, rc, e)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiEntities = append(apiEntities, al)
|
||||
}
|
||||
|
||||
return apiEntities, nil
|
||||
}
|
||||
|
||||
func ListWithHandler(ae *env.AppEnv, rc *response.RequestContext, handler model.Handler, mapper ModelToApiMapper) {
|
||||
List(rc, func(rc *response.RequestContext, queryOptions *model.QueryOptions) (*QueryResult, error) {
|
||||
result, err := handler.BaseList(queryOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiEntities, err := modelToApi(ae, rc, mapper, result.Entities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewQueryResult(apiEntities, &result.QueryMetaData), nil
|
||||
})
|
||||
}
|
||||
|
||||
type ModelListF func(rc *response.RequestContext, queryOptions *model.QueryOptions) (*QueryResult, error)
|
||||
|
||||
func List(rc *response.RequestContext, f ModelListF) {
|
||||
qo, err := GetModelQueryOptionsFromRequest(rc.Request)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("cause", err).Error("could not build query options")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := f(rc, qo)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("cause", err).Error("could not convert list")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if result.Result == nil {
|
||||
result.Result = []BaseApiEntity{}
|
||||
}
|
||||
|
||||
meta := &response.Meta{
|
||||
"pagination": map[string]interface{}{
|
||||
"limit": result.Limit,
|
||||
"offset": result.Offset,
|
||||
"totalCount": result.Count,
|
||||
},
|
||||
"filterableFields": result.FilterableFields,
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(result.Result, meta)
|
||||
}
|
||||
|
||||
type ModelCreateF func() (string, error)
|
||||
|
||||
func Create(rc *response.RequestContext, rr response.RequestResponder, sc *gojsonschema.Schema, in interface{}, lb LinkBuilder, creator ModelCreateF) {
|
||||
var body []byte
|
||||
var err error
|
||||
|
||||
if body, err = ioutil.ReadAll(rc.Request.Body); err != nil {
|
||||
rr.RespondWithCouldNotReadBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = unmarshal(body, in); err != nil {
|
||||
rr.RespondWithCouldNotParseBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
il := gojsonschema.NewGoLoader(in)
|
||||
|
||||
result, err := sc.Validate(il)
|
||||
|
||||
if err != nil {
|
||||
rr.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errs []*apierror.ValidationError
|
||||
for _, re := range result.Errors() {
|
||||
errs = append(errs, apierror.NewValidationError(re))
|
||||
}
|
||||
|
||||
rr.RespondWithValidationErrors(errs)
|
||||
return
|
||||
}
|
||||
|
||||
id, err := creator()
|
||||
if err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
rr.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if fe, ok := err.(*model.FieldError); ok {
|
||||
rr.RespondWithFieldError(fe)
|
||||
return
|
||||
}
|
||||
|
||||
rr.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rr.RespondWithCreatedId(id, lb(id))
|
||||
}
|
||||
|
||||
func DetailWithHandler(ae *env.AppEnv, rc *response.RequestContext, handler model.Handler, mapper ModelToApiMapper, idType response.IdType) {
|
||||
Detail(rc, idType, func(rc *response.RequestContext, id string) (BaseApiEntity, error) {
|
||||
entity, err := handler.BaseLoad(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mapper(ae, rc, entity)
|
||||
})
|
||||
}
|
||||
|
||||
type ModelDetailF func(rc *response.RequestContext, id string) (BaseApiEntity, error)
|
||||
|
||||
func Detail(rc *response.RequestContext, idType response.IdType, f ModelDetailF) {
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
pfxlog.Logger().Error(err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
apiEntity, err := f(rc, id)
|
||||
|
||||
if err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
pfxlog.Logger().WithField("id", id).WithError(err).Error("could not load entity by id")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(apiEntity, nil)
|
||||
}
|
||||
|
||||
type ModelDeleteF func(rc *response.RequestContext, id string) error
|
||||
|
||||
type DeleteHandler interface {
|
||||
HandleDelete(id string) error
|
||||
}
|
||||
|
||||
func DeleteWithHandler(rc *response.RequestContext, idType response.IdType, deleteHandler DeleteHandler) {
|
||||
Delete(rc, idType, func(rc *response.RequestContext, id string) error {
|
||||
return deleteHandler.HandleDelete(id)
|
||||
})
|
||||
}
|
||||
|
||||
func Delete(rc *response.RequestContext, idType response.IdType, deleteF ModelDeleteF) {
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = deleteF(rc, id)
|
||||
|
||||
if err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
} else {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
|
||||
type ModelUpdateF func(id string) error
|
||||
|
||||
func Update(rc *response.RequestContext, sc *gojsonschema.Schema, idType response.IdType, in interface{}, updateF ModelUpdateF) {
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var body []byte
|
||||
|
||||
if body, err = ioutil.ReadAll(rc.Request.Body); err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotReadBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = unmarshal(body, in); err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotParseBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
il := gojsonschema.NewGoLoader(in)
|
||||
|
||||
result, err := sc.Validate(il)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errs []*apierror.ValidationError
|
||||
for _, re := range result.Errors() {
|
||||
errs = append(errs, apierror.NewValidationError(re))
|
||||
}
|
||||
rc.RequestResponder.RespondWithValidationErrors(errs)
|
||||
return
|
||||
}
|
||||
|
||||
if err = updateF(id); err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if fe, ok := err.(*model.FieldError); ok {
|
||||
rc.RequestResponder.RespondWithFieldError(fe)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
|
||||
type ModelPatchF func(id string, fields JsonFields) error
|
||||
|
||||
func Patch(rc *response.RequestContext, sc *gojsonschema.Schema, idType response.IdType, in interface{}, patchF ModelPatchF) {
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var body []byte
|
||||
|
||||
if body, err = ioutil.ReadAll(rc.Request.Body); err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotReadBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = unmarshal(body, in); err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotParseBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonFields, err := getFields(body)
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotParseBody(err)
|
||||
}
|
||||
|
||||
il := gojsonschema.NewGoLoader(in)
|
||||
|
||||
result, err := sc.Validate(il)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errs []*apierror.ValidationError
|
||||
for _, re := range result.Errors() {
|
||||
errs = append(errs, apierror.NewValidationError(re))
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithValidationErrors(errs)
|
||||
return
|
||||
}
|
||||
|
||||
err = patchF(id, jsonFields)
|
||||
if err != nil {
|
||||
if util.IsErrNotFoundErr(err) {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if fe, ok := err.(*model.FieldError); ok {
|
||||
rc.RequestResponder.RespondWithFieldError(fe)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
|
||||
type ListAssocF func(string, func(model.BaseModelEntity)) error
|
||||
|
||||
func ListAssociations(ae *env.AppEnv, rc *response.RequestContext, idType response.IdType, listF ListAssocF, converter ModelToApiMapper) {
|
||||
id, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
logErr := fmt.Errorf("could not find id property: %v", response.IdPropertyName)
|
||||
log.WithField("property", response.IdPropertyName).Error(logErr)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var modelResults []model.BaseModelEntity
|
||||
err = listF(id, func(entity model.BaseModelEntity) {
|
||||
modelResults = append(modelResults, entity)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*util.RecordNotFoundError); ok {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("id", id).WithError(err).Error("could not load associations by id")
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
subApiEs, err := modelToApi(ae, rc, converter, modelResults)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
count := len(modelResults)
|
||||
|
||||
meta := &response.Meta{
|
||||
"pagination": map[string]interface{}{
|
||||
"limit": count,
|
||||
"offset": 0,
|
||||
"totalCount": count,
|
||||
},
|
||||
"filterableFields": []string{},
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(subApiEs, meta)
|
||||
}
|
||||
|
||||
type AssocF func(parentId string, childIds []string) error
|
||||
|
||||
func UpdateAssociationsFor(ae *env.AppEnv, rc *response.RequestContext, idType response.IdType, store persistence.Store, action model.AssociationAction, field string) {
|
||||
UpdateAssociations(ae, rc, idType, func(parentId string, childIds []string) error {
|
||||
return ae.Handlers.Associations.UpdateAssociations(store, action, field, parentId, childIds...)
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateAssociations(ae *env.AppEnv, rc *response.RequestContext, idType response.IdType, assocF AssocF) {
|
||||
parentId, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
logErr := fmt.Errorf("could not find parentId property: %v", response.IdPropertyName)
|
||||
log.WithField("property", response.IdPropertyName).
|
||||
Error(logErr)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(rc.Request.Body)
|
||||
|
||||
in := &associationIdArrayRequest{}
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
}
|
||||
|
||||
il := gojsonschema.NewBytesLoader(body)
|
||||
|
||||
result, err := ae.Schemes.Association.Put.Validate(il)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !result.Valid() {
|
||||
var errs []*apierror.ValidationError
|
||||
for _, re := range result.Errors() {
|
||||
errs = append(errs, apierror.NewValidationError(re))
|
||||
}
|
||||
rc.RequestResponder.RespondWithValidationErrors(errs)
|
||||
return
|
||||
}
|
||||
|
||||
err = unmarshal(body, in)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithCouldNotParseBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
for i, cid := range in.Ids {
|
||||
_, err := uuid.Parse(cid)
|
||||
|
||||
if err != nil {
|
||||
fieldErr := apierror.NewFieldError(fmt.Sprintf("invalid UUID as ID [%s]: %s", cid, err), fmt.Sprintf("ids[%d]", i), cid)
|
||||
rc.RequestResponder.RespondWithApiError(apierror.NewField(fieldErr))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = assocF(parentId, in.Ids)
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*util.RecordNotFoundError); ok {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if fe, ok := err.(*model.FieldError); ok {
|
||||
rc.RequestResponder.RespondWithFieldError(fe)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
|
||||
func RemoveAssociationFor(ae *env.AppEnv, rc *response.RequestContext, idType response.IdType, store persistence.Store, field string) {
|
||||
RemoveAssociationForModel(rc, idType, func(parentId string, childIds []string) error {
|
||||
return ae.Handlers.Associations.UpdateAssociations(store, model.AssociationsActionRemove, field, parentId, childIds...)
|
||||
})
|
||||
}
|
||||
|
||||
func RemoveAssociationForModel(rc *response.RequestContext, idType response.IdType, assocF AssocF) {
|
||||
parentId, err := rc.GetIdFromRequest(idType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
logErr := fmt.Errorf("could not load parent id property: %v", response.IdPropertyName)
|
||||
log.WithField("property", response.IdPropertyName).
|
||||
Error(logErr)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
subId, err := rc.GetSubIdFromRequest()
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
logErr := fmt.Errorf("could not find assigned entity property: %v", response.SubIdPropertyName)
|
||||
log.WithField("property", response.SubIdPropertyName).
|
||||
Error(logErr)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = assocF(parentId, []string{subId})
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*util.RecordNotFoundError); ok {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if fe, ok := err.(*model.FieldError); ok {
|
||||
rc.RequestResponder.RespondWithFieldError(fe)
|
||||
return
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("parentId", parentId).
|
||||
WithField("cause", err).
|
||||
Errorf("could not load parent record by id [%s]: %s", parentId, err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var test = `
|
||||
{
|
||||
"name" : "Foo",
|
||||
"dns" : {
|
||||
"hostname" : "google.com",
|
||||
"port" : 6433
|
||||
},
|
||||
"egressRouter" : "001",
|
||||
"endpointAddress" : null
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
func Test_getFields(t *testing.T) {
|
||||
assert := require.New(t)
|
||||
test2 := ServiceApiCreate{
|
||||
Clusters: []string{"1", "2"},
|
||||
Dns: &ServiceDnsApiPost{
|
||||
Hostname: strPtr("google.com"),
|
||||
Port: uint16Ptr(6433),
|
||||
},
|
||||
Name: strPtr("Foo"),
|
||||
HostIds: nil,
|
||||
Tags: nil,
|
||||
EgressRouter: nil,
|
||||
EndpointAddress: strPtr("tcp:foo:1234"),
|
||||
}
|
||||
|
||||
test2Bytes, err := json.Marshal(test2)
|
||||
assert.NoError(err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
body []byte
|
||||
want JsonFields
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "test",
|
||||
body: []byte(test),
|
||||
want: JsonFields{
|
||||
"Name": true,
|
||||
"Dns.Hostname": true,
|
||||
"Dns.Port": true,
|
||||
"EgressRouter": true,
|
||||
"EndpointAddress": false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
body: test2Bytes,
|
||||
want: JsonFields{
|
||||
"Name": true,
|
||||
"Clusters": true,
|
||||
"Dns.Hostname": true,
|
||||
"Dns.Port": true,
|
||||
"EgressRouter": false,
|
||||
"EndpointAddress": true,
|
||||
"HostIds": false,
|
||||
"Tags": false,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := getFields(tt.body)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getFields() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("getFields() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJsonFields_ConcatNestedNames(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
j JsonFields
|
||||
want JsonFields
|
||||
}{
|
||||
{"test",
|
||||
JsonFields{
|
||||
"Name": true,
|
||||
"Dns.Hostname": true,
|
||||
"Dns.Port": true,
|
||||
"EgressRouter": true,
|
||||
"EndpointAddress": false,
|
||||
},
|
||||
JsonFields{
|
||||
"Name": true,
|
||||
"DnsHostname": true,
|
||||
"DnsPort": true,
|
||||
"EgressRouter": true,
|
||||
"EndpointAddress": false,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.j.ConcatNestedNames()
|
||||
if !reflect.DeepEqual(tt.j, tt.want) {
|
||||
t.Errorf("ConcatNestedNames() got = %v, want %v", tt.j, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/util/stringz"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const EntityNameCa = "cas"
|
||||
|
||||
type CaApiUpdate struct {
|
||||
Tags *migration.PropertyMap `json:"tags"`
|
||||
Name *string `json:"name"`
|
||||
IsAutoCaEnrollmentEnabled *bool `json:"isAutoCaEnrollmentEnabled"`
|
||||
IsOttCaEnrollmentEnabled *bool `json:"isOttCaEnrollmentEnabled"`
|
||||
IsAuthEnabled *bool `json:"isAuthEnabled"`
|
||||
}
|
||||
|
||||
func (i *CaApiUpdate) ToModel(id string) *model.Ca {
|
||||
result := &model.Ca{}
|
||||
result.Id = id
|
||||
result.Name = stringz.OrEmpty(i.Name)
|
||||
if i.Tags != nil {
|
||||
result.Tags = *i.Tags
|
||||
}
|
||||
result.IsAutoCaEnrollmentEnabled = i.IsAutoCaEnrollmentEnabled != nil && *i.IsAutoCaEnrollmentEnabled
|
||||
result.IsOttCaEnrollmentEnabled = i.IsOttCaEnrollmentEnabled != nil && *i.IsOttCaEnrollmentEnabled
|
||||
result.IsAuthEnabled = i.IsAuthEnabled != nil && *i.IsAuthEnabled
|
||||
return result
|
||||
}
|
||||
|
||||
type CaApiCreate struct {
|
||||
Tags *migration.PropertyMap `json:"tags"`
|
||||
Name *string `json:"name"`
|
||||
CertPem *string `json:"certPem"`
|
||||
IsAutoCaEnrollmentEnabled *bool `json:"isAutoCaEnrollmentEnabled"`
|
||||
IsOttCaEnrollmentEnabled *bool `json:"isOttCaEnrollmentEnabled"`
|
||||
IsAuthEnabled *bool `json:"isAuthEnabled"`
|
||||
}
|
||||
|
||||
func (i *CaApiCreate) ToModel() *model.Ca {
|
||||
result := &model.Ca{}
|
||||
result.Name = stringz.OrEmpty(i.Name)
|
||||
if i.Tags != nil {
|
||||
result.Tags = *i.Tags
|
||||
}
|
||||
result.CertPem = stringz.OrEmpty(i.CertPem)
|
||||
result.IsAutoCaEnrollmentEnabled = i.IsAutoCaEnrollmentEnabled != nil && *i.IsAutoCaEnrollmentEnabled
|
||||
result.IsOttCaEnrollmentEnabled = i.IsOttCaEnrollmentEnabled != nil && *i.IsOttCaEnrollmentEnabled
|
||||
result.IsAuthEnabled = i.IsAuthEnabled != nil && *i.IsAuthEnabled
|
||||
return result
|
||||
}
|
||||
|
||||
type CaApiList struct {
|
||||
*env.BaseApi
|
||||
Name *string `json:"name"`
|
||||
Fingerprint *string `json:"fingerprint"`
|
||||
CertPem *string `json:"certPem"`
|
||||
IsVerified *bool `json:"isVerified"`
|
||||
VerificationToken *string `json:"verificationToken"`
|
||||
IsAutoCaEnrollmentEnabled *bool `json:"isAutoCaEnrollmentEnabled"`
|
||||
IsOttCaEnrollmentEnabled *bool `json:"isOttCaEnrollmentEnabled"`
|
||||
IsAuthEnabled *bool `json:"isAuthEnabled"`
|
||||
}
|
||||
|
||||
func (CaApiList) BuildSelfLink(id string) *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s", EntityNameCa, id))
|
||||
}
|
||||
|
||||
func (e *CaApiList) GetSelfLink() *response.Link {
|
||||
return e.BuildSelfLink(e.Id)
|
||||
}
|
||||
|
||||
func (e *CaApiList) PopulateLinks() {
|
||||
if e.Links == nil {
|
||||
e.Links = &response.Links{
|
||||
EntityNameSelf: e.GetSelfLink(),
|
||||
}
|
||||
|
||||
if !*e.IsVerified {
|
||||
vl := response.NewLink(fmt.Sprintf("%s/verify", (*e.Links)[EntityNameSelf].Href))
|
||||
vl.Method = http.MethodPost
|
||||
(*e.Links)["verify"] = vl
|
||||
}
|
||||
|
||||
if *e.IsAutoCaEnrollmentEnabled {
|
||||
jl := response.NewLink(fmt.Sprintf("%s/jwt", (*e.Links)[EntityNameSelf].Href))
|
||||
jl.Method = http.MethodGet
|
||||
(*e.Links)["jwt"] = jl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *CaApiList) ToEntityApiRef() *EntityApiRef {
|
||||
e.PopulateLinks()
|
||||
return &EntityApiRef{
|
||||
Entity: EntityNameCa,
|
||||
Name: e.Name,
|
||||
Id: e.Id,
|
||||
Links: e.Links,
|
||||
}
|
||||
}
|
||||
|
||||
func MapCaToApiEntity(_ *env.AppEnv, _ *response.RequestContext, e model.BaseModelEntity) (BaseApiEntity, error) {
|
||||
i, ok := e.(*model.Ca)
|
||||
|
||||
if !ok {
|
||||
err := fmt.Errorf("entity is not a CA \"%s\"", e.GetId())
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
al, err := MapCaToApiList(i)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("could not convert to API entity \"%s\": %s", e.GetId(), err)
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return al, nil
|
||||
}
|
||||
func MapCaToApiList(i *model.Ca) (*CaApiList, error) {
|
||||
ret := &CaApiList{
|
||||
BaseApi: env.FromBaseModelEntity(i),
|
||||
Name: &i.Name,
|
||||
VerificationToken: &i.VerificationToken,
|
||||
IsVerified: &i.IsVerified,
|
||||
CertPem: &i.CertPem,
|
||||
Fingerprint: &i.Fingerprint,
|
||||
IsAuthEnabled: &i.IsAuthEnabled,
|
||||
IsAutoCaEnrollmentEnabled: &i.IsAutoCaEnrollmentEnabled,
|
||||
IsOttCaEnrollmentEnabled: &i.IsOttCaEnrollmentEnabled,
|
||||
}
|
||||
|
||||
ret.PopulateLinks()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/apierror"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/util"
|
||||
"github.com/netfoundry/ziti-edge/sdk/ziti/config"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
jwt2 "github.com/dgrijalva/jwt-go"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/pkg/errors"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := NewCaRouter()
|
||||
env.AddRouter(r)
|
||||
}
|
||||
|
||||
type CaRouter struct {
|
||||
BasePath string
|
||||
IdType response.IdType
|
||||
}
|
||||
|
||||
func NewCaRouter() *CaRouter {
|
||||
return &CaRouter{
|
||||
BasePath: "/" + EntityNameCa,
|
||||
IdType: response.IdTypeUuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Register(ae *env.AppEnv) {
|
||||
sr := registerCrudRouter(ae, ae.RootRouter, ir.BasePath, ir, permissions.IsAdmin())
|
||||
|
||||
idUrlWithoutSlash := fmt.Sprintf("/{%s}/verify", response.IdPropertyName)
|
||||
idUrlWithSlash := fmt.Sprintf("/{%s}/verify/", response.IdPropertyName)
|
||||
verifyHandler := ae.WrapHandler(ir.VerifyCert, permissions.IsAdmin())
|
||||
sr.HandleFunc(idUrlWithoutSlash, verifyHandler).Methods(http.MethodPost)
|
||||
sr.HandleFunc(idUrlWithSlash, verifyHandler).Methods(http.MethodPost)
|
||||
|
||||
getJwtWithSlash := fmt.Sprintf("/{%s}/jwt", response.IdPropertyName)
|
||||
getJwtWithoutSlash := fmt.Sprintf("/{%s}/jwt/", response.IdPropertyName)
|
||||
jwtHandler := ae.WrapHandler(ir.generateJwt, permissions.IsAdmin())
|
||||
sr.HandleFunc(getJwtWithSlash, jwtHandler).Methods(http.MethodGet)
|
||||
sr.HandleFunc(getJwtWithoutSlash, jwtHandler).Methods(http.MethodGet)
|
||||
}
|
||||
|
||||
func (ir *CaRouter) List(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListWithHandler(ae, rc, ae.Handlers.Ca, MapCaToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DetailWithHandler(ae, rc, ae.Handlers.Ca, MapCaToApiEntity, ir.IdType)
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Create(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &CaApiCreate{}
|
||||
Create(rc, rc.RequestResponder, ae.Schemes.Ca.Post, apiEntity, (&CaApiList{}).BuildSelfLink, func() (string, error) {
|
||||
return ae.Handlers.Ca.HandleCreate(apiEntity.ToModel())
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Delete(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DeleteWithHandler(rc, ir.IdType, ae.Handlers.Ca)
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Update(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &CaApiUpdate{}
|
||||
Update(rc, ae.Schemes.Ca.Put, ir.IdType, apiEntity, func(id string) error {
|
||||
return ae.Handlers.Ca.HandleUpdate(apiEntity.ToModel(id))
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *CaRouter) Patch(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &CaApiUpdate{}
|
||||
Patch(rc, ae.Schemes.Ca.Patch, ir.IdType, apiEntity, func(id string, fields JsonFields) error {
|
||||
return ae.Handlers.Ca.HandlePatch(apiEntity.ToModel(id), fields.ConcatNestedNames())
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *CaRouter) VerifyCert(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
id, err := rc.GetIdFromRequest(ir.IdType)
|
||||
|
||||
if err != nil {
|
||||
log := pfxlog.Logger()
|
||||
err := fmt.Errorf("could not find id property: %v", response.IdPropertyName)
|
||||
log.Error(err)
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
ca, err := ae.Handlers.Ca.HandleRead(id)
|
||||
|
||||
if err != nil {
|
||||
if _, ok := err.(*util.RecordNotFoundError); ok {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
log.WithField("id", id).WithField("cause", err).
|
||||
Errorf("could not load identity by id [%s]: %s", id, err)
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if ca == nil {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if ca.IsVerified {
|
||||
|
||||
rc.RequestResponder.RespondWithApiError(apierror.NewCaAlreadyVerified())
|
||||
return
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(rc.Request.Body)
|
||||
|
||||
if err != nil || len(body) == 0 {
|
||||
rc.RequestResponder.RespondWithCouldNotParseBody(err)
|
||||
return
|
||||
}
|
||||
|
||||
der, _ := pem.Decode(body)
|
||||
|
||||
if der == nil {
|
||||
apiErr := apierror.NewCouldNotParseBody()
|
||||
apiErr.Cause = err
|
||||
apiErr.AppendCause = true
|
||||
rc.RequestResponder.RespondWithApiError(apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
if der.Type != "CERTIFICATE" {
|
||||
apiErr := apierror.NewExpectedPemBlockCertificate()
|
||||
apiErr.Cause = fmt.Errorf("ecountered PEM block type %s", der.Type)
|
||||
rc.RequestResponder.RespondWithApiError(apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(der.Bytes)
|
||||
|
||||
if err != nil {
|
||||
apiErr := apierror.NewCouldNotParseDerBlock()
|
||||
apiErr.AppendCause = true
|
||||
apiErr.Cause = err
|
||||
rc.RequestResponder.RespondWithApiError(apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
if cert.Subject.CommonName != ca.VerificationToken {
|
||||
rc.RequestResponder.RespondWithApiError(apierror.NewInvalidCommonName())
|
||||
return
|
||||
}
|
||||
|
||||
caDer, _ := pem.Decode([]byte(ca.CertPem))
|
||||
|
||||
caCert, err := x509.ParseCertificate(caDer.Bytes)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
roots := x509.NewCertPool()
|
||||
roots.AddCert(caCert)
|
||||
|
||||
_, err = cert.Verify(x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
apiErr := apierror.NewFailedCertificateValidation()
|
||||
apiErr.Cause = err
|
||||
rc.RequestResponder.RespondWithApiError(apiErr)
|
||||
return
|
||||
}
|
||||
|
||||
err = ae.Handlers.Ca.HandleVerified(ca)
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(err)
|
||||
return
|
||||
}
|
||||
|
||||
rc.RequestResponder.RespondWithOk(nil, nil)
|
||||
}
|
||||
|
||||
func (ir *CaRouter) generateJwt(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
id, getErr := rc.GetIdFromRequest(ir.IdType)
|
||||
|
||||
if getErr != nil {
|
||||
log := pfxlog.Logger()
|
||||
err := fmt.Errorf("could not find id property: %v", response.IdPropertyName)
|
||||
log.Error(err)
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
ca, loadErr := ae.Handlers.Ca.HandleRead(id)
|
||||
|
||||
if loadErr != nil {
|
||||
if _, ok := loadErr.(*util.RecordNotFoundError); ok {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
log := pfxlog.Logger()
|
||||
log.Errorf("could not load identity by id \"%s\": %s", id, loadErr)
|
||||
rc.RequestResponder.RespondWithError(loadErr)
|
||||
return
|
||||
}
|
||||
|
||||
if ca == nil {
|
||||
rc.RequestResponder.RespondWithNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
var notAfter int64
|
||||
|
||||
method := "ca"
|
||||
|
||||
claims := &config.EnrollmentClaims{
|
||||
EnrollmentMethod: method,
|
||||
StandardClaims: jwt2.StandardClaims{
|
||||
ExpiresAt: notAfter,
|
||||
Issuer: fmt.Sprintf(`https://%s/`, ae.Config.Api.Advertise),
|
||||
},
|
||||
}
|
||||
mapClaims, err := claims.ToMapClaims()
|
||||
|
||||
if err != nil {
|
||||
rc.RequestResponder.RespondWithError(fmt.Errorf("could not convert CA enrollment claims to interface map: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
jwt, genErr := ae.EnrollmentJwtGenerator.Generate(ca.Id, ca.Id, mapClaims)
|
||||
|
||||
if genErr != nil {
|
||||
rc.RequestResponder.RespondWithError(errors.New("could not generate claims"))
|
||||
return
|
||||
}
|
||||
|
||||
rc.ResponseWriter.Header().Set("Content-Type", "application/jwt")
|
||||
response.AddVersionHeader(rc.ResponseWriter)
|
||||
rc.ResponseWriter.WriteHeader(http.StatusOK)
|
||||
_, _ = rc.ResponseWriter.Write([]byte(jwt))
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/model"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"github.com/netfoundry/ziti-edge/edge/migration"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/pfxlog"
|
||||
"github.com/netfoundry/ziti-foundation/util/stringz"
|
||||
)
|
||||
|
||||
const EntityNameCluster = "clusters"
|
||||
|
||||
type ClusterApi struct {
|
||||
Tags *migration.PropertyMap `json:"tags"`
|
||||
Name *string `json:"name"`
|
||||
}
|
||||
|
||||
func (i *ClusterApi) ToModel(id string) *model.Cluster {
|
||||
result := &model.Cluster{}
|
||||
result.Id = id
|
||||
result.Name = stringz.OrEmpty(i.Name)
|
||||
if i.Tags != nil {
|
||||
result.Tags = *i.Tags
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type ClusterApiList struct {
|
||||
*env.BaseApi
|
||||
Name *string `json:"name"`
|
||||
}
|
||||
|
||||
func (c *ClusterApiList) GetSelfLink() *response.Link {
|
||||
return c.BuildSelfLink(c.Id)
|
||||
}
|
||||
|
||||
func (ClusterApiList) BuildSelfLink(id string) *response.Link {
|
||||
return response.NewLink(fmt.Sprintf("./%s/%s", EntityNameCluster, id))
|
||||
}
|
||||
|
||||
func (c *ClusterApiList) PopulateLinks() {
|
||||
if c.Links == nil {
|
||||
self := c.GetSelfLink()
|
||||
c.Links = &response.Links{
|
||||
EntityNameSelf: self,
|
||||
EntityNameEdgeRouter: response.NewLink(fmt.Sprintf(self.Href + "/" + EntityNameEdgeRouter)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ClusterApiList) ToEntityApiRef() *EntityApiRef {
|
||||
c.PopulateLinks()
|
||||
return &EntityApiRef{
|
||||
Entity: EntityNameCluster,
|
||||
Name: c.Name,
|
||||
Id: c.Id,
|
||||
Links: c.Links,
|
||||
}
|
||||
}
|
||||
|
||||
func MapClusterToApiEntity(_ *env.AppEnv, _ *response.RequestContext, e model.BaseModelEntity) (BaseApiEntity, error) {
|
||||
i, ok := e.(*model.Cluster)
|
||||
|
||||
if !ok {
|
||||
err := fmt.Errorf("entity is not a cluster \"%s\"", e.GetId())
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
al, err := MapClusterToApiList(i)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("could not convert to API entity \"%s\": %s", e.GetId(), err)
|
||||
log := pfxlog.Logger()
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
return al, nil
|
||||
}
|
||||
|
||||
func MapClusterToApiList(i *model.Cluster) (*ClusterApiList, error) {
|
||||
ret := &ClusterApiList{
|
||||
BaseApi: env.FromBaseModelEntity(i),
|
||||
Name: &i.Name,
|
||||
}
|
||||
|
||||
ret.PopulateLinks()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/internal/permissions"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
r := NewClusterRouter()
|
||||
env.AddRouter(r)
|
||||
}
|
||||
|
||||
type ClusterRouter struct {
|
||||
BasePath string
|
||||
IdType response.IdType
|
||||
}
|
||||
|
||||
func NewClusterRouter() *ClusterRouter {
|
||||
return &ClusterRouter{
|
||||
BasePath: "/" + EntityNameCluster,
|
||||
IdType: response.IdTypeUuid,
|
||||
}
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Register(ae *env.AppEnv) {
|
||||
sr := registerCrudRouter(ae, ae.RootRouter, ir.BasePath, ir, permissions.IsAdmin())
|
||||
|
||||
edgeRouterUrlWithSlash := fmt.Sprintf("/{%s}/%s", response.IdPropertyName, EntityNameEdgeRouter)
|
||||
gatewayUrlWithSlash := fmt.Sprintf("/{%s}/%s", response.IdPropertyName, EntityNameGateway)
|
||||
|
||||
edgeRouterWithOutSlash := fmt.Sprintf("/{%s}/%s/", response.IdPropertyName, EntityNameEdgeRouter)
|
||||
gatewayUrlWithOutSlash := fmt.Sprintf("/{%s}/%s/", response.IdPropertyName, EntityNameGateway)
|
||||
|
||||
edgeRoutersListHandler := ae.WrapHandler(ir.ListEdgeRouters, permissions.IsAdmin())
|
||||
edgeRoutersAddHandler := ae.WrapHandler(ir.AddEdgeRouters, permissions.IsAdmin())
|
||||
|
||||
//gets
|
||||
sr.HandleFunc(gatewayUrlWithSlash, edgeRoutersListHandler).Methods(http.MethodGet)
|
||||
sr.HandleFunc(edgeRouterUrlWithSlash, edgeRoutersListHandler).Methods(http.MethodGet)
|
||||
|
||||
sr.HandleFunc(gatewayUrlWithOutSlash, edgeRoutersListHandler).Methods(http.MethodGet)
|
||||
sr.HandleFunc(edgeRouterWithOutSlash, edgeRoutersListHandler).Methods(http.MethodGet)
|
||||
|
||||
//puts
|
||||
sr.HandleFunc(gatewayUrlWithSlash, edgeRoutersAddHandler).Methods(http.MethodPut)
|
||||
sr.HandleFunc(edgeRouterUrlWithSlash, edgeRoutersAddHandler).Methods(http.MethodPut)
|
||||
|
||||
sr.HandleFunc(gatewayUrlWithOutSlash, edgeRoutersAddHandler).Methods(http.MethodPut)
|
||||
sr.HandleFunc(edgeRouterWithOutSlash, edgeRoutersAddHandler).Methods(http.MethodPut)
|
||||
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) List(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListWithHandler(ae, rc, ae.Handlers.Cluster, MapClusterToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Detail(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DetailWithHandler(ae, rc, ae.Handlers.Cluster, MapClusterToApiEntity, ir.IdType)
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Create(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &ClusterApi{}
|
||||
Create(rc, rc.RequestResponder, ae.Schemes.Cluster.Post, apiEntity, (&ClusterApiList{}).BuildSelfLink, func() (string, error) {
|
||||
return ae.Handlers.Cluster.HandleCreate(apiEntity.ToModel(""))
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Delete(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
DeleteWithHandler(rc, ir.IdType, ae.Handlers.Cluster)
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Update(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &ClusterApi{}
|
||||
Update(rc, ae.Schemes.Cluster.Put, ir.IdType, apiEntity, func(id string) error {
|
||||
return ae.Handlers.Cluster.HandleUpdate(apiEntity.ToModel(id))
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) Patch(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
apiEntity := &ClusterApi{}
|
||||
Patch(rc, ae.Schemes.Cluster.Patch, ir.IdType, apiEntity, func(id string, fields JsonFields) error {
|
||||
return ae.Handlers.Cluster.HandlePatch(apiEntity.ToModel(id), fields.ConcatNestedNames())
|
||||
})
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) ListEdgeRouters(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
ListAssociations(ae, rc, ir.IdType, ae.Handlers.Cluster.HandleCollectEdgeRouters, MapEdgeRouterToApiEntity)
|
||||
}
|
||||
|
||||
func (ir *ClusterRouter) AddEdgeRouters(ae *env.AppEnv, rc *response.RequestContext) {
|
||||
UpdateAssociations(ae, rc, ir.IdType, func(parentId string, childIds []string) error {
|
||||
return ae.Handlers.Cluster.HandleAddEdgeRouters(parentId, childIds)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2019 Netfoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package routes
|
||||
|
||||
import (
|
||||
env2 "github.com/netfoundry/ziti-edge/edge/controller/env"
|
||||
"github.com/netfoundry/ziti-edge/edge/controller/response"
|
||||
"time"
|
||||
)
|
||||
|
||||
const EntityNameCurrentSession = "current-session"
|
||||
|
||||
type CurrentSessionApiList struct {
|
||||
*env2.BaseApi
|
||||
Token *string `json:"token"`
|
||||
Identity *EntityApiRef `json:"identity"`
|
||||
ExpiresAt *time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
func (CurrentSessionApiList) BuildSelfLink(_ string) *response.Link {
|
||||
return response.NewLink("./" + EntityNameCurrentSession)
|
||||
}
|
||||
|
||||
func (e *CurrentSessionApiList) GetSelfLink() *response.Link {
|
||||
return e.BuildSelfLink(e.Id)
|
||||
}
|
||||
|
||||
func (e *CurrentSessionApiList) PopulateLinks() {
|
||||
if e.Links == nil {
|
||||
e.Links = &response.Links{
|
||||
EntityNameSelf: e.GetSelfLink(),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (e *CurrentSessionApiList) ToEntityApiRef() *EntityApiRef {
|
||||
e.PopulateLinks()
|
||||
return &EntityApiRef{
|
||||
Entity: EntityNameCurrentSession,
|
||||
Name: nil,
|
||||
Id: e.Id,
|
||||
Links: e.Links,
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user