"git.arvados.org/arvados.git/sdk/go/arvados"
"git.arvados.org/arvados.git/sdk/go/auth"
"git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "git.arvados.org/arvados.git/sdk/go/health"
)
type Conn struct {
remotes map[string]backend
}
-func New(cluster *arvados.Cluster) *Conn {
+func New(cluster *arvados.Cluster, healthFuncs *map[string]health.Func) *Conn {
local := localdb.NewConn(cluster)
remotes := map[string]backend{}
for id, remote := range cluster.RemoteClusters {
remotes[id] = conn
}
+ if healthFuncs != nil {
+ hf := map[string]health.Func{"vocabulary": local.LastVocabularyError}
+ *healthFuncs = hf
+ }
+
return &Conn{
cluster: cluster,
local: local,
return nil, errors.New("no token provided")
}
for _, token := range incoming.Tokens {
- if strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-") && remoteID == cluster.Login.LoginCluster {
- // If we did this, the login cluster
- // would call back to us and then
- // reject our response because the
- // user UUID prefix (i.e., the
- // LoginCluster prefix) won't match
- // the token UUID prefix (i.e., our
- // prefix).
+ if strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-") &&
+ !strings.HasPrefix(token, "v2/"+cluster.ClusterID+"-gj3su-anonymouspublic/") &&
+ remoteID == cluster.Login.LoginCluster {
+ // If we did this, the login cluster would call back to us and then
+ // reject our response because the user UUID prefix (i.e., the
+ // LoginCluster prefix) won't match the token UUID prefix (i.e., our
+ // prefix). The anonymous token is OK to forward, because (unlike other
+ // local tokens for real users) the validation callback will return the
+ // locally issued anonymous user ID instead of a login-cluster user ID.
+ // That anonymous user ID gets mapped to the local anonymous user
+ // automatically on the login cluster.
return nil, httpErrorf(http.StatusUnauthorized, "cannot use a locally issued token to forward a request to our login cluster (%s)", remoteID)
}
salted, err := auth.SaltToken(token, remoteID)
return json.RawMessage(buf.Bytes()), err
}
+func (conn *Conn) VocabularyGet(ctx context.Context) (arvados.Vocabulary, error) {
+ return conn.chooseBackend(conn.cluster.ClusterID).VocabularyGet(ctx)
+}
+
func (conn *Conn) Login(ctx context.Context, options arvados.LoginOptions) (arvados.LoginResponse, error) {
if id := conn.cluster.Login.LoginCluster; id != "" && id != conn.cluster.ClusterID {
// defer entire login procedure to designated cluster
return conn.chooseBackend(options.UUID).GroupUntrash(ctx, options)
}
+func (conn *Conn) LinkCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Link, error) {
+ return conn.chooseBackend(options.ClusterID).LinkCreate(ctx, options)
+}
+
+func (conn *Conn) LinkUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Link, error) {
+ return conn.chooseBackend(options.UUID).LinkUpdate(ctx, options)
+}
+
+func (conn *Conn) LinkGet(ctx context.Context, options arvados.GetOptions) (arvados.Link, error) {
+ return conn.chooseBackend(options.UUID).LinkGet(ctx, options)
+}
+
+func (conn *Conn) LinkList(ctx context.Context, options arvados.ListOptions) (arvados.LinkList, error) {
+ return conn.generated_LinkList(ctx, options)
+}
+
+func (conn *Conn) LinkDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Link, error) {
+ return conn.chooseBackend(options.UUID).LinkDelete(ctx, options)
+}
+
func (conn *Conn) SpecimenList(ctx context.Context, options arvados.ListOptions) (arvados.SpecimenList, error) {
return conn.generated_SpecimenList(ctx, options)
}
return conn.chooseBackend(options.UUID).SpecimenDelete(ctx, options)
}
+func (conn *Conn) SysTrashSweep(ctx context.Context, options struct{}) (struct{}, error) {
+ return conn.local.SysTrashSweep(ctx, options)
+}
+
var userAttrsCachedFromLoginCluster = map[string]bool{
"created_at": true,
"email": true,
"owner_uuid": false,
"uuid": false,
"writable_by": false,
+ "can_write": false,
+ "can_manage": false,
}
func (conn *Conn) batchUpdateUsers(ctx context.Context,
return conn.chooseBackend(options.UUID).APIClientAuthorizationCurrent(ctx, options)
}
+func (conn *Conn) APIClientAuthorizationCreate(ctx context.Context, options arvados.CreateOptions) (arvados.APIClientAuthorization, error) {
+ if conn.cluster.Login.LoginCluster != "" {
+ return conn.chooseBackend(conn.cluster.Login.LoginCluster).APIClientAuthorizationCreate(ctx, options)
+ }
+ ownerUUID, ok := options.Attrs["owner_uuid"].(string)
+ if ok && ownerUUID != "" {
+ return conn.chooseBackend(ownerUUID).APIClientAuthorizationCreate(ctx, options)
+ }
+ return conn.local.APIClientAuthorizationCreate(ctx, options)
+}
+
+func (conn *Conn) APIClientAuthorizationUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.APIClientAuthorization, error) {
+ if options.BypassFederation {
+ return conn.local.APIClientAuthorizationUpdate(ctx, options)
+ }
+ return conn.chooseBackend(options.UUID).APIClientAuthorizationUpdate(ctx, options)
+}
+
+func (conn *Conn) APIClientAuthorizationDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.APIClientAuthorization, error) {
+ return conn.chooseBackend(options.UUID).APIClientAuthorizationDelete(ctx, options)
+}
+
+func (conn *Conn) APIClientAuthorizationList(ctx context.Context, options arvados.ListOptions) (arvados.APIClientAuthorizationList, error) {
+ if id := conn.cluster.Login.LoginCluster; id != "" && id != conn.cluster.ClusterID && !options.BypassFederation {
+ return conn.chooseBackend(conn.cluster.Login.LoginCluster).APIClientAuthorizationList(ctx, options)
+ }
+ return conn.generated_APIClientAuthorizationList(ctx, options)
+}
+
+func (conn *Conn) APIClientAuthorizationGet(ctx context.Context, options arvados.GetOptions) (arvados.APIClientAuthorization, error) {
+ return conn.chooseBackend(options.UUID).APIClientAuthorizationGet(ctx, options)
+}
+
type backend interface {
arvados.API
BaseURL() url.URL