[ARVADOS] created: 2.1.0-1678-g47982d37d

Git user git at public.arvados.org
Wed Nov 24 20:37:50 UTC 2021


        at  47982d37d1124c7615508ca17b299b6f31a654d7 (commit)


commit 47982d37d1124c7615508ca17b299b6f31a654d7
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Wed Nov 24 17:12:36 2021 -0300

    17785: Fixes ApiClientAuthorization.ExpiresAt type.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/lib/controller/federation.go b/lib/controller/federation.go
index cd69727ec..e7d6e29b8 100644
--- a/lib/controller/federation.go
+++ b/lib/controller/federation.go
@@ -214,10 +214,9 @@ VALUES ($1, $2, CURRENT_TIMESTAMP AT TIME ZONE 'UTC' + INTERVAL '2 weeks', $3,
 	}
 
 	return &arvados.APIClientAuthorization{
-		UUID:      uuid,
-		APIToken:  token,
-		ExpiresAt: "",
-		Scopes:    scopes}, nil
+		UUID:     uuid,
+		APIToken: token,
+		Scopes:   scopes}, nil
 }
 
 // Extract the auth token supplied in req, and replace it with a
diff --git a/lib/controller/federation_test.go b/lib/controller/federation_test.go
index eb398695b..a3b198ffc 100644
--- a/lib/controller/federation_test.go
+++ b/lib/controller/federation_test.go
@@ -721,7 +721,7 @@ func (s *FederationSuite) TestCreateRemoteContainerRequestCheckRuntimeToken(c *c
 	var aca arvados.APIClientAuthorization
 	c.Check(json.NewDecoder(resp.Body).Decode(&aca), check.IsNil)
 	c.Check(aca.ExpiresAt, check.NotNil) // Time.Now()+BlobSigningTTL
-	t, _ := time.Parse(time.RFC3339Nano, aca.ExpiresAt)
+	t := aca.ExpiresAt
 	c.Check(t.After(time.Now().Add(s.testHandler.Cluster.API.MaxTokenLifetime.Duration())), check.Equals, true)
 	c.Check(t.Before(time.Now().Add(s.testHandler.Cluster.Collections.BlobSigningTTL.Duration())), check.Equals, true)
 }
diff --git a/lib/controller/localdb/login.go b/lib/controller/localdb/login.go
index 3c7b01baa..2b20491a0 100644
--- a/lib/controller/localdb/login.go
+++ b/lib/controller/localdb/login.go
@@ -147,13 +147,13 @@ func (conn *Conn) CreateAPIClientAuthorization(ctx context.Context, rootToken st
 			tokensecret = tokenparts[2]
 		}
 	}
-	var exp sql.NullString
+	var exp sql.NullTime
 	var scopes []byte
 	err = tx.QueryRowxContext(ctx, "select uuid, api_token, expires_at, scopes from api_client_authorizations where api_token=$1", tokensecret).Scan(&resp.UUID, &resp.APIToken, &exp, &scopes)
 	if err != nil {
 		return
 	}
-	resp.ExpiresAt = exp.String
+	resp.ExpiresAt = exp.Time
 	if len(scopes) > 0 {
 		err = json.Unmarshal(scopes, &resp.Scopes)
 		if err != nil {
diff --git a/lib/controller/localdb/login_oidc.go b/lib/controller/localdb/login_oidc.go
index 6182469ac..e076f7e12 100644
--- a/lib/controller/localdb/login_oidc.go
+++ b/lib/controller/localdb/login_oidc.go
@@ -408,11 +408,8 @@ func (ta *oidcTokenAuthorizer) registerToken(ctx context.Context, tok string) er
 		// cached positive result
 		aca := cached.(arvados.APIClientAuthorization)
 		var expiring bool
-		if aca.ExpiresAt != "" {
-			t, err := time.Parse(time.RFC3339Nano, aca.ExpiresAt)
-			if err != nil {
-				return fmt.Errorf("error parsing expires_at value: %w", err)
-			}
+		if !aca.ExpiresAt.IsZero() {
+			t := aca.ExpiresAt
 			expiring = t.Before(time.Now().Add(time.Minute))
 		}
 		if !expiring {
@@ -505,7 +502,7 @@ func (ta *oidcTokenAuthorizer) registerToken(ctx context.Context, tok string) er
 	if err != nil {
 		return err
 	}
-	aca.ExpiresAt = exp.Format(time.RFC3339Nano)
+	aca.ExpiresAt = exp
 	ta.cache.Add(tok, aca)
 	return nil
 }
diff --git a/sdk/go/arvados/api_client_authorization.go b/sdk/go/arvados/api_client_authorization.go
index 686caf49d..c920d2dc3 100644
--- a/sdk/go/arvados/api_client_authorization.go
+++ b/sdk/go/arvados/api_client_authorization.go
@@ -15,7 +15,7 @@ type APIClientAuthorization struct {
 	CreatedByIPAddress   string    `json:"created_by_ip_address"`
 	DefaultOwnerUUID     string    `json:"default_owner_uuid"`
 	Etag                 string    `json:"etag"`
-	ExpiresAt            string    `json:"expires_at"`
+	ExpiresAt            time.Time `json:"expires_at"`
 	LastUsedAt           time.Time `json:"last_used_at"`
 	LastUsedByIPAddress  string    `json:"last_used_by_ip_address"`
 	ModifiedAt           time.Time `json:"modified_at"`

commit d9c9f862f9bec00285d39dd64d2b677af9aabe92
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Wed Nov 24 16:53:02 2021 -0300

    17785: Fixes ACA type so that it doesn't have pointers to string fields.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/lib/controller/handler_test.go b/lib/controller/handler_test.go
index a456627c0..723e1011f 100644
--- a/lib/controller/handler_test.go
+++ b/lib/controller/handler_test.go
@@ -392,10 +392,30 @@ func (s *HandlerSuite) TestGetObjects(c *check.C) {
 	json.Unmarshal(resp.Body.Bytes(), &ksList)
 	c.Assert(len(ksList.Items), check.Not(check.Equals), 0)
 	ksUUID := ksList.Items[0].UUID
+	// Create a new token for the test user so that we're not comparing
+	// the ones from the fixtures.
+	req = httptest.NewRequest("POST", "/arvados/v1/api_client_authorizations",
+		strings.NewReader(`{
+			"api_client_authorization": {
+				"owner_uuid": "`+arvadostest.AdminUserUUID+`",
+				"created_by_ip_address": "::1",
+				"last_used_by_ip_address": "::1",
+				"default_owner_uuid": "`+arvadostest.AdminUserUUID+`"
+			}
+		}`))
+	req.Header.Set("Authorization", "Bearer "+arvadostest.SystemRootToken)
+	req.Header.Set("Content-type", "application/json")
+	resp = httptest.NewRecorder()
+	s.handler.ServeHTTP(resp, req)
+	c.Assert(resp.Code, check.Equals, http.StatusOK,
+		check.Commentf("%s", resp.Body.String()))
+	var auth arvados.APIClientAuthorization
+	json.Unmarshal(resp.Body.Bytes(), &auth)
+	c.Assert(auth.UUID, check.Not(check.Equals), "")
 
 	testCases := map[string]map[string]bool{
 		"api_clients/" + arvadostest.TrustedWorkbenchAPIClientUUID:     nil,
-		"api_client_authorizations/" + arvadostest.AdminTokenUUID:      nil,
+		"api_client_authorizations/" + auth.UUID:                       {"href": true, "modified_by_client_uuid": true, "modified_by_user_uuid": true},
 		"authorized_keys/" + arvadostest.AdminAuthorizedKeysUUID:       nil,
 		"collections/" + arvadostest.CollectionWithUniqueWordsUUID:     {"href": true},
 		"containers/" + arvadostest.RunningContainerUUID:               nil,
@@ -411,7 +431,8 @@ func (s *HandlerSuite) TestGetObjects(c *check.C) {
 		"workflows/" + arvadostest.WorkflowWithDefinitionYAMLUUID:      nil,
 	}
 	for url, skippedFields := range testCases {
-		s.CheckObjectType(c, "/arvados/v1/"+url, arvadostest.AdminToken, skippedFields)
+		c.Logf("Testing %q", url)
+		s.CheckObjectType(c, "/arvados/v1/"+url, auth.TokenV2(), skippedFields)
 	}
 }
 
diff --git a/sdk/go/arvados/api_client_authorization.go b/sdk/go/arvados/api_client_authorization.go
index 0fb1dd894..686caf49d 100644
--- a/sdk/go/arvados/api_client_authorization.go
+++ b/sdk/go/arvados/api_client_authorization.go
@@ -12,16 +12,15 @@ type APIClientAuthorization struct {
 	APIClientID          int       `json:"api_client_id"`
 	APIToken             string    `json:"api_token"`
 	CreatedAt            time.Time `json:"created_at"`
-	CreatedByIPAddress   *string   `json:"created_by_ip_address"`
-	DefaultOwnerUUID     *string   `json:"default_owner_uuid"`
+	CreatedByIPAddress   string    `json:"created_by_ip_address"`
+	DefaultOwnerUUID     string    `json:"default_owner_uuid"`
 	Etag                 string    `json:"etag"`
 	ExpiresAt            string    `json:"expires_at"`
-	Href                 string    `json:"href"`
 	LastUsedAt           time.Time `json:"last_used_at"`
-	LastUsedByIPAddress  *string   `json:"last_used_by_ip_address"`
+	LastUsedByIPAddress  string    `json:"last_used_by_ip_address"`
 	ModifiedAt           time.Time `json:"modified_at"`
-	ModifiedByClientUUID *string   `json:"modified_by_client_uuid"`
-	ModifiedByUserUUID   *string   `json:"modified_by_user_uuid"`
+	ModifiedByClientUUID string    `json:"modified_by_client_uuid"`
+	ModifiedByUserUUID   string    `json:"modified_by_user_uuid"`
 	OwnerUUID            string    `json:"owner_uuid"`
 	Scopes               []string  `json:"scopes"`
 	UserID               int       `json:"user_id"`

commit 1bc24f2dea52a1df8bb9587d3070399540ec229e
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Tue Nov 23 19:20:11 2021 -0300

    17785: Enhances test.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/lib/controller/integration_test.go b/lib/controller/integration_test.go
index c877d046b..1498da5a2 100644
--- a/lib/controller/integration_test.go
+++ b/lib/controller/integration_test.go
@@ -668,19 +668,17 @@ func (s *IntegrationSuite) TestFederatedApiClientAuthHandling(c *check.C) {
 	conn1 := s.testClusters["z1111"].Conn()
 
 	// Make sure LoginCluster is properly configured
-	for cls := range s.testClusters {
-		if cls == "z1111" || cls == "z3333" {
-			c.Check(
-				s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
-				check.Equals, "z1111",
-				check.Commentf("incorrect LoginCluster config on cluster %q", cls))
-		}
+	for _, cls := range []string{"z1111", "z3333"} {
+		c.Check(
+			s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
+			check.Equals, "z1111",
+			check.Commentf("incorrect LoginCluster config on cluster %q", cls))
 	}
 	// Get user's UUID & attempt to create a token for it on the remote cluster
 	_, _, _, user := s.testClusters["z1111"].UserClients(rootctx1, c, conn1,
 		"user at example.com", true)
 	_, rootclnt3, _ := s.testClusters["z3333"].ClientsWithToken(rootclnt1.AuthToken)
-	var resp interface{}
+	var resp arvados.APIClientAuthorization
 	err := rootclnt3.RequestAndDecode(
 		&resp, "POST", "arvados/v1/api_client_authorizations", nil,
 		map[string]interface{}{
@@ -690,6 +688,20 @@ func (s *IntegrationSuite) TestFederatedApiClientAuthHandling(c *check.C) {
 		},
 	)
 	c.Assert(err, check.IsNil)
+	newTok := resp.TokenV2()
+	c.Assert(newTok, check.Not(check.Equals), "")
+
+	// Confirm the token is from z1111
+	c.Assert(strings.HasPrefix(newTok, "v2/z1111-gj3su-"), check.Equals, true)
+
+	// Confirm the token works and is from the correct user
+	_, rootclnt3bis, _ := s.testClusters["z3333"].ClientsWithToken(newTok)
+	var curUser arvados.User
+	err = rootclnt3bis.RequestAndDecode(
+		&curUser, "GET", "arvados/v1/users/current", nil, nil,
+	)
+	c.Assert(err, check.IsNil)
+	c.Assert(curUser.UUID, check.Equals, user.UUID)
 }
 
 // Test for bug #18076
@@ -700,13 +712,11 @@ func (s *IntegrationSuite) TestStaleCachedUserRecord(c *check.C) {
 	conn3 := s.testClusters["z3333"].Conn()
 
 	// Make sure LoginCluster is properly configured
-	for cls := range s.testClusters {
-		if cls == "z1111" || cls == "z3333" {
-			c.Check(
-				s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
-				check.Equals, "z1111",
-				check.Commentf("incorrect LoginCluster config on cluster %q", cls))
-		}
+	for _, cls := range []string{"z1111", "z3333"} {
+		c.Check(
+			s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
+			check.Equals, "z1111",
+			check.Commentf("incorrect LoginCluster config on cluster %q", cls))
 	}
 
 	for testCaseNr, testCase := range []struct {

commit 9a1056497d7b7ad18769336489d3a6822547b65a
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Mon Nov 22 17:53:29 2021 -0300

    17785: Controller forwards apiClientAuthorization requests to remotes.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/go.mod b/go.mod
index adca449b7..f0fc546dd 100644
--- a/go.mod
+++ b/go.mod
@@ -59,10 +59,10 @@ require (
 	github.com/src-d/gcfg v1.3.0 // indirect
 	github.com/xanzy/ssh-agent v0.1.0 // indirect
 	golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
-	golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
+	golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d
 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
-	golang.org/x/sys v0.0.0-20210603125802-9665404d3644
-	golang.org/x/tools v0.1.2 // indirect
+	golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e
+	golang.org/x/tools v0.1.7 // indirect
 	google.golang.org/api v0.13.0
 	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
 	gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405
diff --git a/go.sum b/go.sum
index 2f575eae9..0965a4696 100644
--- a/go.sum
+++ b/go.sum
@@ -255,6 +255,8 @@ github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.0 h1:OtISOGfH6sOWa1/qXqqAiOIAO6Z5J3AEAE18WAq6BiQ=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -290,6 +292,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjN
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
@@ -319,10 +323,13 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I=
 golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -330,6 +337,8 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -344,6 +353,8 @@ golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/lib/controller/federation/conn.go b/lib/controller/federation/conn.go
index d4155da10..298c693b4 100644
--- a/lib/controller/federation/conn.go
+++ b/lib/controller/federation/conn.go
@@ -729,6 +729,33 @@ func (conn *Conn) APIClientAuthorizationCurrent(ctx context.Context, options arv
 	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) {
+	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) {
+	return conn.local.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
diff --git a/lib/controller/handler.go b/lib/controller/handler.go
index 965ba040e..f5840b34c 100644
--- a/lib/controller/handler.go
+++ b/lib/controller/handler.go
@@ -136,6 +136,8 @@ func (h *Handler) setup() {
 	mux.Handle("/arvados/v1/links/", rtr)
 	mux.Handle("/login", rtr)
 	mux.Handle("/logout", rtr)
+	mux.Handle("/arvados/v1/api_client_authorizations", rtr)
+	mux.Handle("/arvados/v1/api_client_authorizations/", rtr)
 
 	hs := http.NotFoundHandler()
 	hs = prepend(hs, h.proxyRailsAPI)
diff --git a/lib/controller/router/response.go b/lib/controller/router/response.go
index 01126bcb4..c0c599be8 100644
--- a/lib/controller/router/response.go
+++ b/lib/controller/router/response.go
@@ -138,6 +138,7 @@ func (rtr *router) sendError(w http.ResponseWriter, err error) {
 }
 
 var infixMap = map[string]interface{}{
+	"gj3su": arvados.APIClientAuthorization{},
 	"4zz18": arvados.Collection{},
 	"xvhdp": arvados.ContainerRequest{},
 	"dz642": arvados.Container{},
@@ -150,6 +151,11 @@ var infixMap = map[string]interface{}{
 	"7fd4e": arvados.Workflow{},
 }
 
+var specialKindTransforms = map[string]string{
+	"arvados.APIClientAuthorization":     "arvados#apiClientAuthorization",
+	"arvados.APIClientAuthorizationList": "arvados#apiClientAuthorizationList",
+}
+
 var mungeKind = regexp.MustCompile(`\..`)
 
 func kind(resp interface{}) string {
@@ -157,6 +163,9 @@ func kind(resp interface{}) string {
 	if !strings.HasPrefix(t, "arvados.") {
 		return ""
 	}
+	if k, ok := specialKindTransforms[t]; ok {
+		return k
+	}
 	return mungeKind.ReplaceAllStringFunc(t, func(s string) string {
 		// "arvados.CollectionList" => "arvados#collectionList"
 		return "#" + strings.ToLower(s[1:])
diff --git a/lib/controller/router/router.go b/lib/controller/router/router.go
index 02e06279f..2cfcc4fc2 100644
--- a/lib/controller/router/router.go
+++ b/lib/controller/router/router.go
@@ -384,6 +384,48 @@ func (rtr *router) addRoutes() {
 				return rtr.backend.SpecimenDelete(ctx, *opts.(*arvados.DeleteOptions))
 			},
 		},
+		{
+			arvados.EndpointAPIClientAuthorizationCreate,
+			func() interface{} { return &arvados.CreateOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationCreate(ctx, *opts.(*arvados.CreateOptions))
+			},
+		},
+		{
+			arvados.EndpointAPIClientAuthorizationUpdate,
+			func() interface{} { return &arvados.UpdateOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationUpdate(ctx, *opts.(*arvados.UpdateOptions))
+			},
+		},
+		{
+			arvados.EndpointAPIClientAuthorizationDelete,
+			func() interface{} { return &arvados.DeleteOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationDelete(ctx, *opts.(*arvados.DeleteOptions))
+			},
+		},
+		{
+			arvados.EndpointAPIClientAuthorizationList,
+			func() interface{} { return &arvados.ListOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationList(ctx, *opts.(*arvados.ListOptions))
+			},
+		},
+		{
+			arvados.EndpointAPIClientAuthorizationCurrent,
+			func() interface{} { return &arvados.GetOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationCurrent(ctx, *opts.(*arvados.GetOptions))
+			},
+		},
+		{
+			arvados.EndpointAPIClientAuthorizationGet,
+			func() interface{} { return &arvados.GetOptions{} },
+			func(ctx context.Context, opts interface{}) (interface{}, error) {
+				return rtr.backend.APIClientAuthorizationGet(ctx, *opts.(*arvados.GetOptions))
+			},
+		},
 		{
 			arvados.EndpointUserCreate,
 			func() interface{} { return &arvados.CreateOptions{} },
diff --git a/lib/controller/rpc/conn.go b/lib/controller/rpc/conn.go
index 736ef711e..1148068d7 100644
--- a/lib/controller/rpc/conn.go
+++ b/lib/controller/rpc/conn.go
@@ -652,6 +652,36 @@ func (conn *Conn) APIClientAuthorizationCurrent(ctx context.Context, options arv
 	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
 	return resp, err
 }
+func (conn *Conn) APIClientAuthorizationCreate(ctx context.Context, options arvados.CreateOptions) (arvados.APIClientAuthorization, error) {
+	ep := arvados.EndpointAPIClientAuthorizationCreate
+	var resp arvados.APIClientAuthorization
+	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+	return resp, err
+}
+func (conn *Conn) APIClientAuthorizationUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.APIClientAuthorization, error) {
+	ep := arvados.EndpointAPIClientAuthorizationUpdate
+	var resp arvados.APIClientAuthorization
+	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+	return resp, err
+}
+func (conn *Conn) APIClientAuthorizationDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.APIClientAuthorization, error) {
+	ep := arvados.EndpointAPIClientAuthorizationDelete
+	var resp arvados.APIClientAuthorization
+	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+	return resp, err
+}
+func (conn *Conn) APIClientAuthorizationList(ctx context.Context, options arvados.ListOptions) (arvados.APIClientAuthorizationList, error) {
+	ep := arvados.EndpointAPIClientAuthorizationList
+	var resp arvados.APIClientAuthorizationList
+	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+	return resp, err
+}
+func (conn *Conn) APIClientAuthorizationGet(ctx context.Context, options arvados.GetOptions) (arvados.APIClientAuthorization, error) {
+	ep := arvados.EndpointAPIClientAuthorizationGet
+	var resp arvados.APIClientAuthorization
+	err := conn.requestAndDecode(ctx, &resp, ep, nil, options)
+	return resp, err
+}
 
 type UserSessionAuthInfo struct {
 	UserUUID        string    `json:"user_uuid"`
diff --git a/sdk/go/arvados/api.go b/sdk/go/arvados/api.go
index d4af0e7a8..7409b1813 100644
--- a/sdk/go/arvados/api.go
+++ b/sdk/go/arvados/api.go
@@ -85,6 +85,11 @@ var (
 	EndpointUserBatchUpdate               = APIEndpoint{"PATCH", "arvados/v1/users/batch_update", ""}
 	EndpointUserAuthenticate              = APIEndpoint{"POST", "arvados/v1/users/authenticate", ""}
 	EndpointAPIClientAuthorizationCurrent = APIEndpoint{"GET", "arvados/v1/api_client_authorizations/current", ""}
+	EndpointAPIClientAuthorizationCreate  = APIEndpoint{"POST", "arvados/v1/api_client_authorizations", "api_client_authorization"}
+	EndpointAPIClientAuthorizationUpdate  = APIEndpoint{"PUT", "arvados/v1/api_client_authorizations/{uuid}", "api_client_authorization"}
+	EndpointAPIClientAuthorizationList    = APIEndpoint{"GET", "arvados/v1/api_client_authorizations", ""}
+	EndpointAPIClientAuthorizationDelete  = APIEndpoint{"DELETE", "arvados/v1/api_client_authorizations/{uuid}", ""}
+	EndpointAPIClientAuthorizationGet     = APIEndpoint{"GET", "arvados/v1/api_client_authorizations/{uuid}", ""}
 )
 
 type ContainerSSHOptions struct {
@@ -285,4 +290,9 @@ type API interface {
 	UserBatchUpdate(context.Context, UserBatchUpdateOptions) (UserList, error)
 	UserAuthenticate(ctx context.Context, options UserAuthenticateOptions) (APIClientAuthorization, error)
 	APIClientAuthorizationCurrent(ctx context.Context, options GetOptions) (APIClientAuthorization, error)
+	APIClientAuthorizationCreate(ctx context.Context, options CreateOptions) (APIClientAuthorization, error)
+	APIClientAuthorizationList(ctx context.Context, options ListOptions) (APIClientAuthorizationList, error)
+	APIClientAuthorizationDelete(ctx context.Context, options DeleteOptions) (APIClientAuthorization, error)
+	APIClientAuthorizationUpdate(ctx context.Context, options UpdateOptions) (APIClientAuthorization, error)
+	APIClientAuthorizationGet(ctx context.Context, options GetOptions) (APIClientAuthorization, error)
 }
diff --git a/sdk/go/arvados/api_client_authorization.go b/sdk/go/arvados/api_client_authorization.go
index 7c17cdef0..0fb1dd894 100644
--- a/sdk/go/arvados/api_client_authorization.go
+++ b/sdk/go/arvados/api_client_authorization.go
@@ -4,12 +4,27 @@
 
 package arvados
 
+import "time"
+
 // APIClientAuthorization is an arvados#apiClientAuthorization resource.
 type APIClientAuthorization struct {
-	UUID      string   `json:"uuid"`
-	APIToken  string   `json:"api_token"`
-	ExpiresAt string   `json:"expires_at"`
-	Scopes    []string `json:"scopes"`
+	UUID                 string    `json:"uuid"`
+	APIClientID          int       `json:"api_client_id"`
+	APIToken             string    `json:"api_token"`
+	CreatedAt            time.Time `json:"created_at"`
+	CreatedByIPAddress   *string   `json:"created_by_ip_address"`
+	DefaultOwnerUUID     *string   `json:"default_owner_uuid"`
+	Etag                 string    `json:"etag"`
+	ExpiresAt            string    `json:"expires_at"`
+	Href                 string    `json:"href"`
+	LastUsedAt           time.Time `json:"last_used_at"`
+	LastUsedByIPAddress  *string   `json:"last_used_by_ip_address"`
+	ModifiedAt           time.Time `json:"modified_at"`
+	ModifiedByClientUUID *string   `json:"modified_by_client_uuid"`
+	ModifiedByUserUUID   *string   `json:"modified_by_user_uuid"`
+	OwnerUUID            string    `json:"owner_uuid"`
+	Scopes               []string  `json:"scopes"`
+	UserID               int       `json:"user_id"`
 }
 
 // APIClientAuthorizationList is an arvados#apiClientAuthorizationList resource.
diff --git a/sdk/go/arvadostest/api.go b/sdk/go/arvadostest/api.go
index 6990a3fdf..f49d29ce2 100644
--- a/sdk/go/arvadostest/api.go
+++ b/sdk/go/arvadostest/api.go
@@ -269,6 +269,26 @@ func (as *APIStub) APIClientAuthorizationCurrent(ctx context.Context, options ar
 	as.appendCall(ctx, as.APIClientAuthorizationCurrent, options)
 	return arvados.APIClientAuthorization{}, as.Error
 }
+func (as *APIStub) APIClientAuthorizationCreate(ctx context.Context, options arvados.CreateOptions) (arvados.APIClientAuthorization, error) {
+	as.appendCall(ctx, as.APIClientAuthorizationCreate, options)
+	return arvados.APIClientAuthorization{}, as.Error
+}
+func (as *APIStub) APIClientAuthorizationUpdate(ctx context.Context, options arvados.UpdateOptions) (arvados.APIClientAuthorization, error) {
+	as.appendCall(ctx, as.APIClientAuthorizationUpdate, options)
+	return arvados.APIClientAuthorization{}, as.Error
+}
+func (as *APIStub) APIClientAuthorizationDelete(ctx context.Context, options arvados.DeleteOptions) (arvados.APIClientAuthorization, error) {
+	as.appendCall(ctx, as.APIClientAuthorizationDelete, options)
+	return arvados.APIClientAuthorization{}, as.Error
+}
+func (as *APIStub) APIClientAuthorizationList(ctx context.Context, options arvados.ListOptions) (arvados.APIClientAuthorizationList, error) {
+	as.appendCall(ctx, as.APIClientAuthorizationList, options)
+	return arvados.APIClientAuthorizationList{}, as.Error
+}
+func (as *APIStub) APIClientAuthorizationGet(ctx context.Context, options arvados.GetOptions) (arvados.APIClientAuthorization, error) {
+	as.appendCall(ctx, as.APIClientAuthorizationGet, options)
+	return arvados.APIClientAuthorization{}, as.Error
+}
 
 func (as *APIStub) appendCall(ctx context.Context, method interface{}, options interface{}) {
 	as.mtx.Lock()

commit e39da1ce43d8e695290a50493dc0f7b3c8a739c1
Author: Lucas Di Pentima <lucas.dipentima at curii.com>
Date:   Wed Sep 8 19:50:17 2021 -0300

    17785: Adds test exposing a problem when trying to handle other users' tokens.
    
    Trying to reproduce the "Login as user" feature from wb1 on a satellite
    cluster: the admin user creates an api_client_authorization object owned
    by the target user.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima at curii.com>

diff --git a/lib/controller/integration_test.go b/lib/controller/integration_test.go
index 4cf6a6832..c877d046b 100644
--- a/lib/controller/integration_test.go
+++ b/lib/controller/integration_test.go
@@ -662,6 +662,36 @@ func (s *IntegrationSuite) TestIntermediateCluster(c *check.C) {
 	}
 }
 
+// Test for #17785
+func (s *IntegrationSuite) TestFederatedApiClientAuthHandling(c *check.C) {
+	rootctx1, rootclnt1, _ := s.testClusters["z1111"].RootClients()
+	conn1 := s.testClusters["z1111"].Conn()
+
+	// Make sure LoginCluster is properly configured
+	for cls := range s.testClusters {
+		if cls == "z1111" || cls == "z3333" {
+			c.Check(
+				s.testClusters[cls].Config.Clusters[cls].Login.LoginCluster,
+				check.Equals, "z1111",
+				check.Commentf("incorrect LoginCluster config on cluster %q", cls))
+		}
+	}
+	// Get user's UUID & attempt to create a token for it on the remote cluster
+	_, _, _, user := s.testClusters["z1111"].UserClients(rootctx1, c, conn1,
+		"user at example.com", true)
+	_, rootclnt3, _ := s.testClusters["z3333"].ClientsWithToken(rootclnt1.AuthToken)
+	var resp interface{}
+	err := rootclnt3.RequestAndDecode(
+		&resp, "POST", "arvados/v1/api_client_authorizations", nil,
+		map[string]interface{}{
+			"api_client_authorization": map[string]string{
+				"owner_uuid": user.UUID,
+			},
+		},
+	)
+	c.Assert(err, check.IsNil)
+}
+
 // Test for bug #18076
 func (s *IntegrationSuite) TestStaleCachedUserRecord(c *check.C) {
 	rootctx1, _, _ := s.testClusters["z1111"].RootClients()

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list