X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/625076c05698e37beb732850dc01248be9dbe7f6..8b17f2ab7bcfcad4dcf9e3aa2cc203417053b23e:/lib/controller/federation/conn.go diff --git a/lib/controller/federation/conn.go b/lib/controller/federation/conn.go index 61cac9bbab..31faa04b81 100644 --- a/lib/controller/federation/conn.go +++ b/lib/controller/federation/conn.go @@ -79,6 +79,14 @@ func saltedTokenProvider(local backend, remoteID string) rpc.TokenProvider { } else if err != nil { return nil, err } + if strings.HasPrefix(aca.UUID, remoteID) { + // We have it cached here, but + // the token belongs to the + // remote target itself, so + // pass it through unmodified. + tokens = append(tokens, token) + continue + } salted, err := auth.SaltToken(aca.TokenV2(), remoteID) if err != nil { return nil, err @@ -328,6 +336,104 @@ func (conn *Conn) ContainerUnlock(ctx context.Context, options arvados.GetOption return conn.chooseBackend(options.UUID).ContainerUnlock(ctx, options) } +func (conn *Conn) ContainerSSH(ctx context.Context, options arvados.ContainerSSHOptions) (arvados.ContainerSSHConnection, error) { + return conn.chooseBackend(options.UUID).ContainerSSH(ctx, options) +} + +func (conn *Conn) ContainerRequestList(ctx context.Context, options arvados.ListOptions) (arvados.ContainerRequestList, error) { + return conn.generated_ContainerRequestList(ctx, options) +} + +func (conn *Conn) ContainerRequestCreate(ctx context.Context, options arvados.CreateOptions) (arvados.ContainerRequest, error) { + be := conn.chooseBackend(options.ClusterID) + if be == conn.local { + return be.ContainerRequestCreate(ctx, options) + } + if _, ok := options.Attrs["runtime_token"]; !ok { + // If runtime_token is not set, create a new token + aca, err := conn.local.APIClientAuthorizationCurrent(ctx, arvados.GetOptions{}) + if err != nil { + // This should probably be StatusUnauthorized + // (need to update test in + // lib/controller/federation_test.go): + // When RoR is out of the picture this should be: + // return arvados.ContainerRequest{}, httpErrorf(http.StatusUnauthorized, "%w", err) + return arvados.ContainerRequest{}, httpErrorf(http.StatusForbidden, "%s", "invalid API token") + } + user, err := conn.local.UserGetCurrent(ctx, arvados.GetOptions{}) + if err != nil { + return arvados.ContainerRequest{}, err + } + if len(aca.Scopes) == 0 || aca.Scopes[0] != "all" { + return arvados.ContainerRequest{}, httpErrorf(http.StatusForbidden, "token scope is not [all]") + } + if strings.HasPrefix(aca.UUID, conn.cluster.ClusterID) { + // Local user, submitting to a remote cluster. + // Create a new time-limited token. + local, ok := conn.local.(*localdb.Conn) + if !ok { + return arvados.ContainerRequest{}, httpErrorf(http.StatusInternalServerError, "bug: local backend is a %T, not a *localdb.Conn", conn.local) + } + aca, err = local.CreateAPIClientAuthorization(ctx, conn.cluster.SystemRootToken, rpc.UserSessionAuthInfo{UserUUID: user.UUID, + ExpiresAt: time.Now().UTC().Add(conn.cluster.Collections.BlobSigningTTL.Duration())}) + if err != nil { + return arvados.ContainerRequest{}, err + } + options.Attrs["runtime_token"] = aca.TokenV2() + } else { + // Remote user. Container request will use the + // current token, minus the trailing portion + // (optional container uuid). + options.Attrs["runtime_token"] = aca.TokenV2() + } + } + return be.ContainerRequestCreate(ctx, options) +} + +func (conn *Conn) ContainerRequestUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.ContainerRequest, error) { + return conn.chooseBackend(options.UUID).ContainerRequestUpdate(ctx, options) +} + +func (conn *Conn) ContainerRequestGet(ctx context.Context, options arvados.GetOptions) (arvados.ContainerRequest, error) { + return conn.chooseBackend(options.UUID).ContainerRequestGet(ctx, options) +} + +func (conn *Conn) ContainerRequestDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.ContainerRequest, error) { + return conn.chooseBackend(options.UUID).ContainerRequestDelete(ctx, options) +} + +func (conn *Conn) GroupCreate(ctx context.Context, options arvados.CreateOptions) (arvados.Group, error) { + return conn.chooseBackend(options.ClusterID).GroupCreate(ctx, options) +} + +func (conn *Conn) GroupUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.Group, error) { + return conn.chooseBackend(options.UUID).GroupUpdate(ctx, options) +} + +func (conn *Conn) GroupGet(ctx context.Context, options arvados.GetOptions) (arvados.Group, error) { + return conn.chooseBackend(options.UUID).GroupGet(ctx, options) +} + +func (conn *Conn) GroupList(ctx context.Context, options arvados.ListOptions) (arvados.GroupList, error) { + return conn.generated_GroupList(ctx, options) +} + +func (conn *Conn) GroupContents(ctx context.Context, options arvados.ContentsOptions) (arvados.ObjectList, error) { + return conn.chooseBackend(options.UUID).GroupContents(ctx, options) +} + +func (conn *Conn) GroupShared(ctx context.Context, options arvados.SharedOptions) (arvados.ObjectList, error) { + return conn.chooseBackend(options.UUID).GroupShared(ctx, options) +} + +func (conn *Conn) GroupDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.Group, error) { + return conn.chooseBackend(options.UUID).GroupDelete(ctx, options) +} + +func (conn *Conn) GroupUntrash(ctx context.Context, options arvados.UntrashOptions) (arvados.Group, error) { + return conn.chooseBackend(options.UUID).GroupUntrash(ctx, options) +} + func (conn *Conn) SpecimenList(ctx context.Context, options arvados.ListOptions) (arvados.SpecimenList, error) { return conn.generated_SpecimenList(ctx, options) } @@ -454,7 +560,18 @@ func (conn *Conn) UserUpdate(ctx context.Context, options arvados.UpdateOptions) if options.BypassFederation { return conn.local.UserUpdate(ctx, options) } - return conn.chooseBackend(options.UUID).UserUpdate(ctx, options) + resp, err := conn.chooseBackend(options.UUID).UserUpdate(ctx, options) + if err != nil { + return resp, err + } + if !strings.HasPrefix(options.UUID, conn.cluster.ClusterID) { + // Copy the updated user record to the local cluster + err = conn.batchUpdateUsers(ctx, arvados.ListOptions{}, []arvados.User{resp}) + if err != nil { + return arvados.User{}, err + } + } + return resp, err } func (conn *Conn) UserUpdateUUID(ctx context.Context, options arvados.UpdateUUIDOptions) (arvados.User, error) { @@ -500,11 +617,24 @@ func (conn *Conn) UserUnsetup(ctx context.Context, options arvados.GetOptions) ( } func (conn *Conn) UserGet(ctx context.Context, options arvados.GetOptions) (arvados.User, error) { - return conn.chooseBackend(options.UUID).UserGet(ctx, options) + resp, err := conn.chooseBackend(options.UUID).UserGet(ctx, options) + if err != nil { + return resp, err + } + if options.UUID != resp.UUID { + return arvados.User{}, httpErrorf(http.StatusBadGateway, "Had requested %v but response was for %v", options.UUID, resp.UUID) + } + if options.UUID[:5] != conn.cluster.ClusterID { + err = conn.batchUpdateUsers(ctx, arvados.ListOptions{Select: options.Select}, []arvados.User{resp}) + if err != nil { + return arvados.User{}, err + } + } + return resp, nil } func (conn *Conn) UserGetCurrent(ctx context.Context, options arvados.GetOptions) (arvados.User, error) { - return conn.chooseBackend(options.UUID).UserGetCurrent(ctx, options) + return conn.local.UserGetCurrent(ctx, options) } func (conn *Conn) UserGetSystem(ctx context.Context, options arvados.GetOptions) (arvados.User, error) {