mirror of
https://github.com/openziti/zrok.git
synced 2026-08-24 02:34:20 -05:00
653 lines
22 KiB
Go
653 lines
22 KiB
Go
package controller
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/go-openapi/runtime/middleware"
|
|
"github.com/jmoiron/sqlx"
|
|
"github.com/michaelquigley/df/dl"
|
|
"github.com/openziti/edge-api/rest_model"
|
|
"github.com/openziti/zrok/v2/controller/automation"
|
|
"github.com/openziti/zrok/v2/controller/store"
|
|
"github.com/openziti/zrok/v2/rest_model_zrok"
|
|
"github.com/openziti/zrok/v2/rest_server_zrok/operations/share"
|
|
"github.com/openziti/zrok/v2/sdk/golang/sdk"
|
|
"github.com/openziti/zrok/v2/util"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type shareHandler struct{}
|
|
|
|
func newShareHandler() *shareHandler {
|
|
return &shareHandler{}
|
|
}
|
|
|
|
func (h *shareHandler) Handle(params share.ShareParams, principal *rest_model_zrok.Principal) middleware.Responder {
|
|
trx, err := str.Begin()
|
|
if err != nil {
|
|
dl.Errorf("error starting transaction: %v", err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
defer func() { _ = trx.Rollback() }()
|
|
|
|
// validate environment
|
|
envZId := params.Body.EnvZID
|
|
envId, err := h.validateEnvironment(envZId, principal, trx)
|
|
if err != nil {
|
|
dl.Errorf("environment validation failed: %v", err)
|
|
return share.NewShareUnauthorized()
|
|
}
|
|
|
|
// check limits
|
|
if err := h.checkLimits(envId, principal, params, trx); err != nil {
|
|
dl.Errorf("limits error: %v", err)
|
|
return share.NewShareUnauthorized()
|
|
}
|
|
|
|
// create share token
|
|
var shrToken string
|
|
if sdk.ShareMode(params.Body.ShareMode) == sdk.PrivateShareMode && params.Body.PrivateShareToken != "" {
|
|
dl.Infof("private share requested share token '%v'", params.Body.PrivateShareToken)
|
|
if util.IsValidShareToken(params.Body.PrivateShareToken, cfg.Names != nil && cfg.Names.DisableShareTokenProfanityCheck) {
|
|
shrToken = params.Body.PrivateShareToken
|
|
} else {
|
|
dl.Errorf("requested private share token '%v' has invalid unique name", params.Body.PrivateShareToken)
|
|
return share.NewShareConflict().WithPayload(rest_model_zrok.ErrorMessage(fmt.Sprintf("requested private share token '%v' has invalid unique name", params.Body.PrivateShareToken)))
|
|
}
|
|
} else {
|
|
shrToken, err = createShareToken()
|
|
if err != nil {
|
|
dl.Error(err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
}
|
|
|
|
// process namespace selections
|
|
var frontendEndpoints []string
|
|
var nameIds []int
|
|
if sdk.ShareMode(params.Body.ShareMode) == sdk.PublicShareMode {
|
|
frontendEndpoints, nameIds, err = h.processNameSelections(params.Body.NameSelections, shrToken, principal, trx)
|
|
if err != nil {
|
|
dl.Errorf("namespace selection processing failed: %v", err)
|
|
return share.NewShareConflict().WithPayload(rest_model_zrok.ErrorMessage(err.Error()))
|
|
}
|
|
}
|
|
|
|
// allocate resources based on share mode
|
|
var shrZId string
|
|
switch sdk.ShareMode(params.Body.ShareMode) {
|
|
case sdk.PublicShareMode:
|
|
interstitial, err := h.shouldUseInterstitial(params.Body.BackendMode, principal, trx)
|
|
if err != nil {
|
|
dl.Errorf("error determining interstitial setting for account '%v': %v", principal.Email, err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
shrZId, frontendEndpoints, err = h.allocatePublicResources(envZId, shrToken, frontendEndpoints, params, interstitial, trx)
|
|
|
|
case sdk.PrivateShareMode:
|
|
// check private share token availability if provided
|
|
if params.Body.PrivateShareToken != "" {
|
|
if err := h.checkPrivateShareTokenAvailability(shrToken); err != nil {
|
|
dl.Errorf("private share token conflict: %v", err)
|
|
return share.NewShareConflict().WithPayload(rest_model_zrok.ErrorMessage(err.Error()))
|
|
}
|
|
}
|
|
shrZId, frontendEndpoints, err = h.allocatePrivateResources(envZId, shrToken, frontendEndpoints, params, trx)
|
|
|
|
default:
|
|
dl.Errorf("unknown share mode '%v'", params.Body.ShareMode)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
if err != nil {
|
|
dl.Errorf("error allocating share resources: %v", err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
|
|
// create share record
|
|
shareId, err := h.createShareRecord(envId, shrZId, shrToken, params, frontendEndpoints, trx)
|
|
if err != nil {
|
|
dl.Errorf("error creating share record: %v", err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
|
|
if sdk.ShareMode(params.Body.ShareMode) == sdk.PublicShareMode {
|
|
// create share name mappings for namespace selections
|
|
for _, nameId := range nameIds {
|
|
snm := &store.ShareNameMapping{
|
|
ShareId: shareId,
|
|
NameId: nameId,
|
|
}
|
|
_, err := str.CreateShareNameMapping(snm, trx)
|
|
if err != nil {
|
|
dl.Errorf("error creating share name mapping for share '%v' and name '%v': %v", shareId, nameId, err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
}
|
|
|
|
// send mapping updates to dynamic frontends after successful commit
|
|
if err := h.processDynamicMappings(shrToken, nameIds, trx); err != nil {
|
|
dl.Errorf("error sending mapping updates: %v", err)
|
|
}
|
|
}
|
|
|
|
// handle access grants if closed permission mode
|
|
if err := h.processAccessGrants(shareId, params.Body.AccessGrants, params.Body.PermissionMode, principal, trx); err != nil {
|
|
dl.Errorf("error processing access grants: %v", err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
|
|
if err := trx.Commit(); err != nil {
|
|
dl.Errorf("error committing share record: %v", err)
|
|
return share.NewShareInternalServerError()
|
|
}
|
|
|
|
dl.Infof("recorded share '%v' with id '%v' for '%v'", shrToken, shareId, principal.Email)
|
|
|
|
return share.NewShareCreated().WithPayload(&rest_model_zrok.ShareResponse{
|
|
FrontendProxyEndpoints: frontendEndpoints,
|
|
ShareToken: shrToken,
|
|
})
|
|
}
|
|
|
|
func (h *shareHandler) validateEnvironment(envZId string, principal *rest_model_zrok.Principal, trx *sqlx.Tx) (int, error) {
|
|
env, err := str.FindEnvironmentForAccount(envZId, int(principal.ID), trx)
|
|
if err != nil {
|
|
return 0, errors.Wrapf(err, "error finding environment '%v' for account '%v'", envZId, principal.Email)
|
|
}
|
|
return env.Id, nil
|
|
}
|
|
|
|
func (h *shareHandler) checkLimits(envId int, principal *rest_model_zrok.Principal, params share.ShareParams, trx *sqlx.Tx) error {
|
|
if !principal.Limitless {
|
|
if limitsAgent != nil {
|
|
shareMode := sdk.ShareMode(params.Body.ShareMode)
|
|
backendMode := sdk.BackendMode(params.Body.BackendMode)
|
|
|
|
// we're going to skip reservation checking because we're moving name creation outside the scope of share
|
|
// creation. the limits check for name creation will happen in the `/share/name` endpoint instead.
|
|
ok, err := limitsAgent.CanCreateShare(int(principal.ID), envId, false, false, shareMode, backendMode, trx)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error checking share limits for '%v'", principal.Email)
|
|
}
|
|
if !ok {
|
|
return errors.Errorf("share limit check failed for '%v'", principal.Email)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *shareHandler) shouldUseInterstitial(backendMode string, principal *rest_model_zrok.Principal, trx *sqlx.Tx) (bool, error) {
|
|
var skipInterstitial bool
|
|
parsedBackendMode := sdk.BackendMode(backendMode)
|
|
|
|
if parsedBackendMode != sdk.DriveBackendMode {
|
|
var err error
|
|
skipInterstitial, err = str.IsAccountGrantedSkipInterstitial(int(principal.ID), trx)
|
|
if err != nil {
|
|
return false, errors.Wrapf(err, "error checking skip interstitial for account '%v'", principal.Email)
|
|
}
|
|
} else {
|
|
// always skip interstitial for drive backend mode
|
|
skipInterstitial = true
|
|
}
|
|
|
|
return !skipInterstitial, nil
|
|
}
|
|
|
|
func (h *shareHandler) processNameSelections(selections []*rest_model_zrok.NameSelection, shrToken string, principal *rest_model_zrok.Principal, trx *sqlx.Tx) ([]string, []int, error) {
|
|
var frontendEndpoints []string
|
|
var nameIds []int
|
|
|
|
for _, selection := range selections {
|
|
// find namespace by token
|
|
ns, err := str.FindNamespaceWithToken(selection.NamespaceToken, trx)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "error finding namespace with token '%v'", selection.NamespaceToken)
|
|
}
|
|
|
|
var endpoint string
|
|
var nameId int
|
|
|
|
if selection.Name != "" { // user specified a name - validate ownership and availability
|
|
name, err := str.FindNameByNamespaceAndName(ns.Id, selection.Name, trx)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "error finding name '%v' in namespace '%v'", selection.Name, ns.Token)
|
|
}
|
|
|
|
// check if user owns this name
|
|
if name.AccountId != int(principal.ID) {
|
|
return nil, nil, errors.Errorf("user '%v' does not own name '%v' in namespace '%v'", principal.Email, selection.Name, ns.Token)
|
|
}
|
|
|
|
// check if there's already a share_name_mapping for this name
|
|
existing, err := str.FindShareNameMappingsByNameId(name.Id, trx)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "error checking existing share name mappings for name '%v'", selection.Name)
|
|
}
|
|
if len(existing) > 0 {
|
|
return nil, nil, errors.Errorf("name '%v' in namespace '%v' is already in use by another share", selection.Name, ns.Token)
|
|
}
|
|
|
|
nameId = name.Id
|
|
endpoint = util.NameInNamespace(name.Name, ns.Name)
|
|
|
|
} else { // no name specified - generate one and create name record
|
|
// check namespace permissions
|
|
if !ns.Open {
|
|
granted, err := str.CheckNamespaceGrant(ns.Id, int(principal.ID), trx)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "error checking namespace grant for account '%v' and namespace '%v'", principal.Email, ns.Token)
|
|
}
|
|
if !granted {
|
|
return nil, nil, errors.Errorf("account '%v' is not granted access to namespace '%v'", principal.Email, ns.Token)
|
|
}
|
|
}
|
|
|
|
// create name record with reserved=false (dynamically allocated)
|
|
name := &store.Name{
|
|
NamespaceId: ns.Id,
|
|
Name: shrToken,
|
|
AccountId: int(principal.ID),
|
|
Reserved: false,
|
|
}
|
|
|
|
nameId, err = str.CreateName(name, trx)
|
|
if err != nil {
|
|
return nil, nil, errors.Wrapf(err, "error creating allocated name '%v' in namespace '%v' for account '%v'", shrToken, ns.Token, principal.Email)
|
|
}
|
|
|
|
endpoint = util.NameInNamespace(shrToken, ns.Name)
|
|
}
|
|
|
|
frontendEndpoints = append(frontendEndpoints, endpoint)
|
|
nameIds = append(nameIds, nameId)
|
|
}
|
|
|
|
return frontendEndpoints, nameIds, nil
|
|
}
|
|
|
|
func (h *shareHandler) allocatePublicResources(envZId, shrToken string, frontendEndpoints []string, params share.ShareParams, interstitial bool, trx interface{}) (string, []string, error) {
|
|
// get shared automation client
|
|
ziti, err := automation.NewZitiAutomation(cfg.Ziti)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error getting ziti automation client")
|
|
}
|
|
|
|
// prepare auth users
|
|
var authUsers []*sdk.AuthUserConfig
|
|
for _, authUser := range params.Body.BasicAuthUsers {
|
|
authUsers = append(authUsers, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
|
}
|
|
|
|
// parse auth scheme
|
|
authScheme, err := sdk.ParseAuthScheme(params.Body.AuthScheme)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error parsing auth scheme")
|
|
}
|
|
|
|
// prepare oauth config
|
|
var oauthCfg *sdk.OauthConfig
|
|
if authScheme == sdk.Oauth {
|
|
oauthCfg = &sdk.OauthConfig{
|
|
Provider: params.Body.OauthProvider,
|
|
EmailDomains: params.Body.OauthEmailDomains,
|
|
AuthorizationCheckInterval: params.Body.OauthRefreshInterval,
|
|
}
|
|
}
|
|
|
|
// create frontend config
|
|
frontendConfig := &sdk.FrontendConfig{
|
|
Interstitial: interstitial,
|
|
AuthScheme: authScheme,
|
|
}
|
|
if authScheme == sdk.Basic {
|
|
frontendConfig.BasicAuth = &sdk.BasicAuthConfig{Users: authUsers}
|
|
}
|
|
if authScheme == sdk.Oauth && oauthCfg != nil {
|
|
frontendConfig.OauthAuth = oauthCfg
|
|
}
|
|
|
|
// create config using the global zrokProxyConfigId
|
|
tags := automation.ZrokShareTags(shrToken)
|
|
configOpts := &automation.ConfigOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: shrToken,
|
|
Tags: tags,
|
|
},
|
|
ConfigTypeID: zrokProxyConfigId,
|
|
Data: frontendConfig,
|
|
}
|
|
cfgZId, err := ziti.Configs.Create(configOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating config")
|
|
}
|
|
|
|
// create share service
|
|
serviceOpts := &automation.ServiceOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: shrToken,
|
|
Tags: tags,
|
|
},
|
|
Configs: []string{cfgZId},
|
|
EncryptionRequired: true,
|
|
}
|
|
shrZId, err := ziti.Services.Create(serviceOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating share service")
|
|
}
|
|
|
|
// create bind policy (backend can bind to this service)
|
|
bindPolicyName := envZId + "-" + shrZId + "-bind"
|
|
bindPolicyOpts := &automation.ServicePolicyOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: bindPolicyName,
|
|
Tags: tags,
|
|
},
|
|
IdentityRoles: []string{"@" + envZId},
|
|
ServiceRoles: []string{"@" + shrZId},
|
|
PolicyType: rest_model.DialBindBind,
|
|
Semantic: rest_model.SemanticAllOf,
|
|
}
|
|
_, err = ziti.ServicePolicies.Create(bindPolicyOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating service policy bind")
|
|
}
|
|
|
|
// create dial policy (frontends can dial this service)
|
|
// get frontend identities from namespaces
|
|
var frontendZIds []string
|
|
for _, selection := range params.Body.NameSelections {
|
|
ns, err := str.FindNamespaceWithToken(selection.NamespaceToken, trx.(*sqlx.Tx))
|
|
if err != nil {
|
|
return "", nil, errors.Wrapf(err, "error finding namespace with token '%v'", selection.NamespaceToken)
|
|
}
|
|
|
|
frontends, err := str.FindFrontendsForNamespace(ns.Id, trx.(*sqlx.Tx))
|
|
if err != nil {
|
|
return "", nil, errors.Wrapf(err, "error finding frontends for namespace '%v'", ns.Token)
|
|
}
|
|
|
|
for _, fe := range frontends {
|
|
frontendZIds = append(frontendZIds, fe.ZId)
|
|
}
|
|
}
|
|
|
|
if len(frontendZIds) > 0 {
|
|
dialPolicyName := envZId + "-" + shrZId + "-dial"
|
|
var dialIdentityRoles []string
|
|
for _, frontendZId := range frontendZIds {
|
|
dialIdentityRoles = append(dialIdentityRoles, "@"+frontendZId)
|
|
}
|
|
dialPolicyOpts := &automation.ServicePolicyOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: dialPolicyName,
|
|
Tags: tags,
|
|
},
|
|
IdentityRoles: dialIdentityRoles,
|
|
ServiceRoles: []string{"@" + shrZId},
|
|
PolicyType: rest_model.DialBindDial,
|
|
Semantic: rest_model.SemanticAllOf,
|
|
}
|
|
_, err = ziti.ServicePolicies.Create(dialPolicyOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating service policy dial")
|
|
}
|
|
}
|
|
|
|
// create service edge router policy
|
|
serpPolicyName := envZId + "-" + shrToken + "-serp"
|
|
serpPolicyOpts := &automation.ServiceEdgeRouterPolicyOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: serpPolicyName,
|
|
Tags: tags,
|
|
},
|
|
ServiceRoles: []string{"@" + shrZId},
|
|
EdgeRouterRoles: []string{"#all"},
|
|
Semantic: rest_model.SemanticAllOf,
|
|
}
|
|
_, err = ziti.ServiceEdgeRouterPolicies.Create(serpPolicyOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating service edge router policy")
|
|
}
|
|
|
|
dl.Infof("allocated public resources for share '%v' with service id '%v'", shrToken, shrZId)
|
|
return shrZId, frontendEndpoints, nil
|
|
}
|
|
|
|
func (h *shareHandler) checkPrivateShareTokenAvailability(privateShareToken string) error {
|
|
ziti, err := automation.NewZitiAutomation(cfg.Ziti)
|
|
if err != nil {
|
|
return errors.Wrap(err, "error getting ziti automation client")
|
|
}
|
|
|
|
// verify the service name is available
|
|
_, err = ziti.Services.GetByName(privateShareToken)
|
|
if err == nil {
|
|
return errors.Errorf("service name '%v' is already in use", privateShareToken)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *shareHandler) allocatePrivateResources(envZId, shrToken string, frontendEndpoints []string, params share.ShareParams, trx interface{}) (string, []string, error) {
|
|
// get shared automation client
|
|
ziti, err := automation.NewZitiAutomation(cfg.Ziti)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error getting ziti automation client")
|
|
}
|
|
|
|
// prepare auth users
|
|
var authUsers []*sdk.AuthUserConfig
|
|
for _, authUser := range params.Body.BasicAuthUsers {
|
|
authUsers = append(authUsers, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
|
}
|
|
|
|
// parse auth scheme
|
|
authScheme, err := sdk.ParseAuthScheme(params.Body.AuthScheme)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error parsing auth scheme")
|
|
}
|
|
|
|
// prepare oauth config
|
|
var oauthCfg *sdk.OauthConfig
|
|
if authScheme == sdk.Oauth {
|
|
oauthCfg = &sdk.OauthConfig{
|
|
Provider: params.Body.OauthProvider,
|
|
EmailDomains: params.Body.OauthEmailDomains,
|
|
AuthorizationCheckInterval: params.Body.OauthRefreshInterval,
|
|
}
|
|
}
|
|
|
|
// create frontend config (private shares don't use interstitials)
|
|
frontendConfig := &sdk.FrontendConfig{
|
|
Interstitial: false,
|
|
AuthScheme: authScheme,
|
|
}
|
|
if authScheme == sdk.Basic {
|
|
frontendConfig.BasicAuth = &sdk.BasicAuthConfig{Users: authUsers}
|
|
}
|
|
if authScheme == sdk.Oauth && oauthCfg != nil {
|
|
frontendConfig.OauthAuth = oauthCfg
|
|
}
|
|
|
|
// create config using the global zrokProxyConfigId
|
|
tags := automation.ZrokShareTags(shrToken)
|
|
configOpts := &automation.ConfigOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: shrToken,
|
|
Tags: tags,
|
|
},
|
|
ConfigTypeID: zrokProxyConfigId,
|
|
Data: frontendConfig,
|
|
}
|
|
cfgZId, err := ziti.Configs.Create(configOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating config")
|
|
}
|
|
|
|
// create share service
|
|
serviceOpts := &automation.ServiceOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: shrToken,
|
|
Tags: tags,
|
|
},
|
|
Configs: []string{cfgZId},
|
|
EncryptionRequired: true,
|
|
}
|
|
shrZId, err := ziti.Services.Create(serviceOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating share service")
|
|
}
|
|
|
|
// create bind policy (backend can bind to this service)
|
|
bindPolicyName := envZId + "-" + shrZId + "-bind"
|
|
bindPolicyOpts := &automation.ServicePolicyOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: bindPolicyName,
|
|
Tags: tags,
|
|
},
|
|
IdentityRoles: []string{"@" + envZId},
|
|
ServiceRoles: []string{"@" + shrZId},
|
|
PolicyType: rest_model.DialBindBind,
|
|
Semantic: rest_model.SemanticAllOf,
|
|
}
|
|
_, err = ziti.ServicePolicies.Create(bindPolicyOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating service policy bind")
|
|
}
|
|
|
|
// create service edge router policy
|
|
serpPolicyName := envZId + "-" + shrToken + "-serp"
|
|
serpPolicyOpts := &automation.ServiceEdgeRouterPolicyOptions{
|
|
BaseOptions: automation.BaseOptions{
|
|
Name: serpPolicyName,
|
|
Tags: tags,
|
|
},
|
|
ServiceRoles: []string{"@" + shrZId},
|
|
EdgeRouterRoles: []string{"#all"},
|
|
Semantic: rest_model.SemanticAllOf,
|
|
}
|
|
_, err = ziti.ServiceEdgeRouterPolicies.Create(serpPolicyOpts)
|
|
if err != nil {
|
|
return "", nil, errors.Wrap(err, "error creating service edge router policy")
|
|
}
|
|
|
|
// note: private shares don't create dial policies here
|
|
// dial access is granted separately via the access endpoint
|
|
|
|
dl.Infof("allocated private resources for share '%v' with service id '%v'", shrToken, shrZId)
|
|
return shrZId, frontendEndpoints, nil
|
|
}
|
|
|
|
func (h *shareHandler) createShareRecord(envId int, shrZId, shrToken string, params share.ShareParams, frontendEndpoints []string, trx interface{}) (int, error) {
|
|
strShr := &store.Share{
|
|
ZId: shrZId,
|
|
Token: shrToken,
|
|
ShareMode: params.Body.ShareMode,
|
|
BackendMode: params.Body.BackendMode,
|
|
PermissionMode: store.OpenPermissionMode,
|
|
}
|
|
|
|
// set target as backend proxy endpoint for share12
|
|
if params.Body.Target != "" {
|
|
strShr.BackendProxyEndpoint = ¶ms.Body.Target
|
|
}
|
|
|
|
// set permission mode if specified
|
|
if params.Body.PermissionMode != "" {
|
|
strShr.PermissionMode = store.PermissionMode(params.Body.PermissionMode)
|
|
}
|
|
|
|
// set frontend endpoint (first one if multiple)
|
|
if len(frontendEndpoints) > 0 {
|
|
strShr.FrontendEndpoint = &frontendEndpoints[0]
|
|
} else if strShr.ShareMode == "private" {
|
|
// for private shares without frontend endpoints, use the share mode as endpoint
|
|
strShr.FrontendEndpoint = &strShr.ShareMode
|
|
}
|
|
|
|
// create the share record
|
|
shareId, err := str.CreateShare(envId, strShr, trx.(*sqlx.Tx))
|
|
if err != nil {
|
|
return 0, errors.Wrap(err, "error creating share record")
|
|
}
|
|
|
|
dl.Infof("created share record with id '%v' for share '%v'", shareId, shrToken)
|
|
return shareId, nil
|
|
}
|
|
|
|
func (h *shareHandler) processAccessGrants(shareId int, accessGrants []string, permissionMode string, principal *rest_model_zrok.Principal, trx interface{}) error {
|
|
// only process access grants for closed permission mode
|
|
if store.PermissionMode(permissionMode) != store.ClosedPermissionMode {
|
|
return nil
|
|
}
|
|
|
|
// find account IDs for the access grant email addresses
|
|
var accessGrantAcctIds []int
|
|
for _, email := range accessGrants {
|
|
acct, err := str.FindAccountWithEmail(email, trx.(*sqlx.Tx))
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to find account '%v' for share request from '%v'", email, principal.Email)
|
|
}
|
|
dl.Debugf("found id '%d' for '%v'", acct.Id, acct.Email)
|
|
accessGrantAcctIds = append(accessGrantAcctIds, acct.Id)
|
|
}
|
|
|
|
// create access grants for each account
|
|
for _, acctId := range accessGrantAcctIds {
|
|
_, err := str.CreateAccessGrant(shareId, acctId, trx.(*sqlx.Tx))
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error creating access grant for share '%v' and account '%v'", shareId, acctId)
|
|
}
|
|
dl.Debugf("created access grant for share '%v' and account '%v'", shareId, acctId)
|
|
}
|
|
|
|
if len(accessGrantAcctIds) > 0 {
|
|
dl.Infof("created %d access grants for closed share '%v'", len(accessGrantAcctIds), shareId)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *shareHandler) processDynamicMappings(shrToken string, nameIds []int, trx *sqlx.Tx) error {
|
|
// only send updates if dynamic proxy controller is enabled
|
|
if dPCtrl == nil {
|
|
dl.Warnf("dynamic proxy controller is nil")
|
|
return nil
|
|
}
|
|
|
|
for _, nameId := range nameIds {
|
|
// find name record to get the name and namespace
|
|
name, err := str.GetName(nameId, trx)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error finding name with id '%v'", nameId)
|
|
}
|
|
|
|
// find namespace
|
|
ns, err := str.GetNamespace(name.NamespaceId, trx)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error finding namespace with id '%v'", name.NamespaceId)
|
|
}
|
|
|
|
// find dynamic frontends for this namespace
|
|
frontends, err := str.FindDynamicFrontendsForNamespace(ns.Id, trx)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error finding dynamic frontends for namespace '%v'", ns.Token)
|
|
}
|
|
|
|
// send mapping updates to each dynamic frontend
|
|
for _, frontend := range frontends {
|
|
frontendName := util.NameInNamespace(name.Name, ns.Name)
|
|
dl.Infof("binding name '%v'", frontendName)
|
|
|
|
if err := dPCtrl.BindFrontendMapping(frontend.Token, frontendName, shrToken, trx); err != nil {
|
|
dl.Errorf("error binding frontend mapping to frontend '%v': %v", frontend.Token, err)
|
|
// continue with other frontends rather than failing completely
|
|
} else {
|
|
dl.Infof("bound frontend mapping '%v' to dynamic frontend '%v'", frontendName, frontend.Token)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|