diff --git a/controller/grants.go b/controller/grants.go index b4f74d56..7bc3853a 100644 --- a/controller/grants.go +++ b/controller/grants.go @@ -2,8 +2,10 @@ package controller import ( "github.com/go-openapi/runtime/middleware" + "github.com/openziti/zrok/controller/zrokEdgeSdk" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/rest_server_zrok/operations/admin" + "github.com/openziti/zrok/sdk/golang/sdk" "github.com/sirupsen/logrus" ) @@ -18,5 +20,67 @@ func (h *grantsHandler) Handle(params admin.GrantsParams, principal *rest_model_ logrus.Errorf("invalid admin principal") return admin.NewGrantsUnauthorized() } + + edge, err := zrokEdgeSdk.Client(cfg.Ziti) + if err != nil { + logrus.Errorf("error connecting to ziti: %v", err) + return admin.NewGrantsInternalServerError() + } + + trx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return admin.NewGrantsInternalServerError() + } + defer func() { _ = trx.Rollback() }() + + acct, err := str.FindAccountWithEmail(params.Body.Email, trx) + if err != nil { + logrus.Errorf("error finding account with email '%v': %v", params.Body.Email, err) + return admin.NewGrantsNotFound() + } + + acctSkipInterstitial, err := str.IsAccountGrantedSkipInterstitial(acct.Id, trx) + if err != nil { + logrus.Errorf("error checking account '%v' granted skip interstitial: %v", acct.Email, err) + } + + envs, err := str.FindEnvironmentsForAccount(acct.Id, trx) + if err != nil { + logrus.Errorf("error finding environments for '%v': %v", acct.Email, err) + return admin.NewGrantsInternalServerError() + } + + for _, env := range envs { + shrs, err := str.FindSharesForEnvironment(env.Id, trx) + if err != nil { + logrus.Errorf("error finding shares for '%v': %v", acct.Email, err) + return admin.NewGrantsInternalServerError() + } + + for _, shr := range shrs { + if shr.ShareMode == string(sdk.PublicShareMode) && shr.BackendMode != string(sdk.DriveBackendMode) { + cfgZId, shrCfg, err := zrokEdgeSdk.GetConfig(shr.Token, edge) + if err != nil { + logrus.Errorf("error getting config for share '%v': %v", shr.Token, err) + return admin.NewGrantsInternalServerError() + } + + if shrCfg.Interstitial != !acctSkipInterstitial { + logrus.Infof("updating config for '%v'", shr.Token) + err := zrokEdgeSdk.UpdateConfig(cfgZId, shrCfg, edge) + if err != nil { + logrus.Errorf("error updating config for '%v': %v", shr.Token, err) + return admin.NewGrantsInternalServerError() + } + } else { + logrus.Infof("skipping config update for '%v'", shr.Token) + } + } else { + logrus.Debugf("skipping share mode %v, backend mode %v", shr.ShareMode, shr.BackendMode) + } + } + } + return admin.NewGrantsOK() } diff --git a/controller/zrokEdgeSdk/config.go b/controller/zrokEdgeSdk/config.go index d58f32ee..24e8d46d 100644 --- a/controller/zrokEdgeSdk/config.go +++ b/controller/zrokEdgeSdk/config.go @@ -8,6 +8,7 @@ import ( "github.com/openziti/edge-api/rest_model" "github.com/openziti/zrok/sdk/golang/sdk" "github.com/sirupsen/logrus" + "reflect" "time" ) @@ -55,6 +56,41 @@ func CreateConfig(cfgTypeZId, envZId, shrToken string, options *FrontendOptions, return cfgResp.Payload.Data.ID, nil } +func GetConfig(shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) (string, *sdk.FrontendConfig, error) { + filter := fmt.Sprintf("tags.zrokShareToken=\"%v\"", shrToken) + limit := int64(0) + offset := int64(0) + listReq := &config.ListConfigsParams{ + Filter: &filter, + Limit: &limit, + Offset: &offset, + Context: context.Background(), + } + listReq.SetTimeout(30 * time.Second) + listResp, err := edge.Config.ListConfigs(listReq, nil) + if err != nil { + return "", nil, err + } + if len(listResp.Payload.Data) != 1 { + return "", nil, fmt.Errorf("expected 1 configuration, found %v", len(listResp.Payload.Data)) + } + if listResp.Payload.Data[0].ConfigType.Name != sdk.ZrokProxyConfig { + return "", nil, fmt.Errorf("expected '%v', found '%v'", sdk.ZrokProxyConfig, listResp.Payload.Data[0].ConfigType.Name) + } + if v, ok := listResp.Payload.Data[0].Data.(map[string]interface{}); ok { + fec, err := sdk.FrontendConfigFromMap(v) + if err != nil { + return "", nil, err + } + return *listResp.Payload.Data[0].ID, fec, nil + } + return "", nil, fmt.Errorf("unknown data type '%v' unmarshaling config for '%v'", reflect.TypeOf(listResp.Payload.Data[0].Data), shrToken) +} + +func UpdateConfig(cfgZId string, cfg *sdk.FrontendConfig, edge *rest_management_api_client.ZitiEdgeManagement) error { + return nil +} + func DeleteConfig(envZId, shrToken string, edge *rest_management_api_client.ZitiEdgeManagement) error { filter := fmt.Sprintf("tags.zrokShareToken=\"%v\"", shrToken) limit := int64(0) diff --git a/rest_client_zrok/admin/grants_responses.go b/rest_client_zrok/admin/grants_responses.go index 79354d88..a8b1ddb1 100644 --- a/rest_client_zrok/admin/grants_responses.go +++ b/rest_client_zrok/admin/grants_responses.go @@ -34,6 +34,12 @@ func (o *GrantsReader) ReadResponse(response runtime.ClientResponse, consumer ru return nil, err } return nil, result + case 404: + result := NewGrantsNotFound() + if err := result.readResponse(response, consumer, o.formats); err != nil { + return nil, err + } + return nil, result case 500: result := NewGrantsInternalServerError() if err := result.readResponse(response, consumer, o.formats); err != nil { @@ -157,6 +163,62 @@ func (o *GrantsUnauthorized) readResponse(response runtime.ClientResponse, consu return nil } +// NewGrantsNotFound creates a GrantsNotFound with default headers values +func NewGrantsNotFound() *GrantsNotFound { + return &GrantsNotFound{} +} + +/* +GrantsNotFound describes a response with status code 404, with default header values. + +not found +*/ +type GrantsNotFound struct { +} + +// IsSuccess returns true when this grants not found response has a 2xx status code +func (o *GrantsNotFound) IsSuccess() bool { + return false +} + +// IsRedirect returns true when this grants not found response has a 3xx status code +func (o *GrantsNotFound) IsRedirect() bool { + return false +} + +// IsClientError returns true when this grants not found response has a 4xx status code +func (o *GrantsNotFound) IsClientError() bool { + return true +} + +// IsServerError returns true when this grants not found response has a 5xx status code +func (o *GrantsNotFound) IsServerError() bool { + return false +} + +// IsCode returns true when this grants not found response a status code equal to that given +func (o *GrantsNotFound) IsCode(code int) bool { + return code == 404 +} + +// Code gets the status code for the grants not found response +func (o *GrantsNotFound) Code() int { + return 404 +} + +func (o *GrantsNotFound) Error() string { + return fmt.Sprintf("[POST /grants][%d] grantsNotFound ", 404) +} + +func (o *GrantsNotFound) String() string { + return fmt.Sprintf("[POST /grants][%d] grantsNotFound ", 404) +} + +func (o *GrantsNotFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { + + return nil +} + // NewGrantsInternalServerError creates a GrantsInternalServerError with default headers values func NewGrantsInternalServerError() *GrantsInternalServerError { return &GrantsInternalServerError{} diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index ed565e45..efc2bb0c 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -557,6 +557,9 @@ func init() { "401": { "description": "unauthorized" }, + "404": { + "description": "not found" + }, "500": { "description": "internal server error" } @@ -2404,6 +2407,9 @@ func init() { "401": { "description": "unauthorized" }, + "404": { + "description": "not found" + }, "500": { "description": "internal server error" } diff --git a/rest_server_zrok/operations/admin/grants_responses.go b/rest_server_zrok/operations/admin/grants_responses.go index 37fd7af1..f7682f7a 100644 --- a/rest_server_zrok/operations/admin/grants_responses.go +++ b/rest_server_zrok/operations/admin/grants_responses.go @@ -61,6 +61,31 @@ func (o *GrantsUnauthorized) WriteResponse(rw http.ResponseWriter, producer runt rw.WriteHeader(401) } +// GrantsNotFoundCode is the HTTP code returned for type GrantsNotFound +const GrantsNotFoundCode int = 404 + +/* +GrantsNotFound not found + +swagger:response grantsNotFound +*/ +type GrantsNotFound struct { +} + +// NewGrantsNotFound creates GrantsNotFound with default headers values +func NewGrantsNotFound() *GrantsNotFound { + + return &GrantsNotFound{} +} + +// WriteResponse to the client +func (o *GrantsNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses + + rw.WriteHeader(404) +} + // GrantsInternalServerErrorCode is the HTTP code returned for type GrantsInternalServerError const GrantsInternalServerErrorCode int = 500 diff --git a/sdk/golang/sdk/config.go b/sdk/golang/sdk/config.go index e07000bb..f701d5f7 100644 --- a/sdk/golang/sdk/config.go +++ b/sdk/golang/sdk/config.go @@ -2,6 +2,8 @@ package sdk import ( "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "reflect" ) const ZrokProxyConfig = "zrok.proxy.v1" @@ -13,21 +15,133 @@ type FrontendConfig struct { OauthAuth *OauthConfig `json:"oauth"` } +func FrontendConfigFromMap(m map[string]interface{}) (*FrontendConfig, error) { + logrus.Info(m) + out := &FrontendConfig{} + if v, found := m["interstitial"]; found { + out.Interstitial = v.(bool) + } + if v, found := m["auth_scheme"]; found { + if vStr, ok := v.(string); ok { + out.AuthScheme = AuthScheme(vStr) + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + if v, found := m["basic_auth"]; found && v != nil { + if subMap, ok := v.(map[string]interface{}); ok { + ba, err := BasicAuthConfigFromMap(subMap) + if err != nil { + return nil, err + } + out.BasicAuth = ba + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + if v, found := m["oauth"]; found && v != nil { + if subMap, ok := v.(map[string]interface{}); ok { + o, err := OauthConfigFromMap(subMap) + if err != nil { + return nil, err + } + out.OauthAuth = o + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + return out, nil +} + type BasicAuthConfig struct { Users []*AuthUserConfig `json:"users"` } +func BasicAuthConfigFromMap(m map[string]interface{}) (*BasicAuthConfig, error) { + out := &BasicAuthConfig{} + if v, found := m["basic_auth"]; found { + if vArr, ok := v.([]interface{}); ok { + for _, vV := range vArr { + if v, ok := vV.(map[string]interface{}); ok { + if auc, err := AuthUserConfigFromMap(v); err == nil { + out.Users = append(out.Users, auc) + } else { + return nil, err + } + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + return out, nil + } + return nil, nil +} + type AuthUserConfig struct { Username string `json:"username"` Password string `json:"password"` } +func AuthUserConfigFromMap(m map[string]interface{}) (*AuthUserConfig, error) { + auc := &AuthUserConfig{} + if v, found := m["username"]; found { + if vStr, ok := v.(string); ok { + auc.Username = vStr + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + if v, found := m["password"]; found { + if vStr, ok := v.(string); ok { + auc.Password = vStr + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + return auc, nil +} + type OauthConfig struct { Provider string `json:"provider"` EmailDomains []string `json:"email_domains"` AuthorizationCheckInterval string `json:"authorization_check_interval"` } +func OauthConfigFromMap(m map[string]interface{}) (*OauthConfig, error) { + oac := &OauthConfig{} + if v, found := m["provider"]; found { + if vStr, ok := v.(string); ok { + oac.Provider = vStr + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + if v, found := m["email_domains"]; found { + if vArr, ok := v.([]interface{}); ok { + for _, vV := range vArr { + if vStr, ok := vV.(string); ok { + oac.EmailDomains = append(oac.EmailDomains, vStr) + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(vV)) + } + } + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + if v, found := m["authorization_check_interval"]; found { + if vStr, ok := v.(string); ok { + oac.AuthorizationCheckInterval = vStr + } else { + return nil, errors.Errorf("unexpected type '%v'", reflect.TypeOf(v)) + } + } + return oac, nil +} + func ParseAuthScheme(authScheme string) (AuthScheme, error) { switch authScheme { case string(None): diff --git a/specs/zrok.yml b/specs/zrok.yml index cc9ef0f8..853d1059 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -326,6 +326,8 @@ paths: description: ok 401: description: unauthorized + 404: + description: not found 500: description: internal server error