d5fed3e29f819c83709a141e5e9c951635cb8586
[arvados.git] / tools / sync-groups / federation_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "bytes"
9         "net"
10         "os"
11         "path/filepath"
12
13         "git.arvados.org/arvados.git/lib/boot"
14         "git.arvados.org/arvados.git/lib/config"
15         "git.arvados.org/arvados.git/sdk/go/arvados"
16         "git.arvados.org/arvados.git/sdk/go/arvadostest"
17         "git.arvados.org/arvados.git/sdk/go/ctxlog"
18         check "gopkg.in/check.v1"
19 )
20
21 var _ = check.Suite(&FederationSuite{})
22
23 var origAPIHost, origAPIToken string
24
25 type FederationSuite struct {
26         testClusters map[string]*boot.TestCluster
27         oidcprovider *arvadostest.OIDCProvider
28 }
29
30 func (s *FederationSuite) SetUpSuite(c *check.C) {
31         origAPIHost = os.Getenv("ARVADOS_API_HOST")
32         origAPIToken = os.Getenv("ARVADOS_API_TOKEN")
33
34         cwd, _ := os.Getwd()
35
36         s.oidcprovider = arvadostest.NewOIDCProvider(c)
37         s.oidcprovider.AuthEmail = "user@example.com"
38         s.oidcprovider.AuthEmailVerified = true
39         s.oidcprovider.AuthName = "Example User"
40         s.oidcprovider.ValidClientID = "clientid"
41         s.oidcprovider.ValidClientSecret = "clientsecret"
42
43         s.testClusters = map[string]*boot.TestCluster{
44                 "z1111": nil,
45                 "z2222": nil,
46         }
47         hostport := map[string]string{}
48         for id := range s.testClusters {
49                 hostport[id] = func() string {
50                         // TODO: Instead of expecting random ports on
51                         // 127.0.0.11, 22 to be race-safe, try
52                         // different 127.x.y.z until finding one that
53                         // isn't in use.
54                         ln, err := net.Listen("tcp", ":0")
55                         c.Assert(err, check.IsNil)
56                         ln.Close()
57                         _, port, err := net.SplitHostPort(ln.Addr().String())
58                         c.Assert(err, check.IsNil)
59                         return "127.0.0." + id[3:] + ":" + port
60                 }()
61         }
62         for id := range s.testClusters {
63                 yaml := `Clusters:
64   ` + id + `:
65     Services:
66       Controller:
67         ExternalURL: https://` + hostport[id] + `
68     TLS:
69       Insecure: true
70     SystemLogs:
71       Format: text
72     RemoteClusters:
73       z1111:
74         Host: ` + hostport["z1111"] + `
75         Scheme: https
76         Insecure: true
77         Proxy: true
78         ActivateUsers: true
79 `
80                 if id != "z2222" {
81                         yaml += `      z2222:
82         Host: ` + hostport["z2222"] + `
83         Scheme: https
84         Insecure: true
85         Proxy: true
86         ActivateUsers: true
87 `
88                 }
89                 if id == "z1111" {
90                         yaml += `
91     Login:
92       LoginCluster: z1111
93       OpenIDConnect:
94         Enable: true
95         Issuer: ` + s.oidcprovider.Issuer.URL + `
96         ClientID: ` + s.oidcprovider.ValidClientID + `
97         ClientSecret: ` + s.oidcprovider.ValidClientSecret + `
98         EmailClaim: email
99         EmailVerifiedClaim: email_verified
100 `
101                 } else {
102                         yaml += `
103     Login:
104       LoginCluster: z1111
105 `
106                 }
107
108                 loader := config.NewLoader(bytes.NewBufferString(yaml), ctxlog.TestLogger(c))
109                 loader.Path = "-"
110                 loader.SkipLegacy = true
111                 loader.SkipAPICalls = true
112                 cfg, err := loader.Load()
113                 c.Assert(err, check.IsNil)
114                 tc := boot.NewTestCluster(
115                         filepath.Join(cwd, "..", ".."),
116                         id, cfg, "127.0.0."+id[3:], c.Log)
117                 tc.Super.NoWorkbench1 = true
118                 tc.Super.NoWorkbench2 = true
119                 tc.Start()
120                 s.testClusters[id] = tc
121         }
122         for _, tc := range s.testClusters {
123                 ok := tc.WaitReady()
124                 c.Assert(ok, check.Equals, true)
125         }
126
127         // Activate user, make it admin.
128         conn1 := s.testClusters["z1111"].Conn()
129         rootctx1, _, _ := s.testClusters["z1111"].RootClients()
130         userctx1, _, _, _ := s.testClusters["z1111"].UserClients(rootctx1, c, conn1, s.oidcprovider.AuthEmail, true)
131         user1, err := conn1.UserGetCurrent(userctx1, arvados.GetOptions{})
132         c.Assert(err, check.IsNil)
133         c.Assert(user1.IsAdmin, check.Equals, false)
134         user1, err = conn1.UserUpdate(rootctx1, arvados.UpdateOptions{
135                 UUID: user1.UUID,
136                 Attrs: map[string]interface{}{
137                         "is_admin": true,
138                 },
139         })
140         c.Assert(err, check.IsNil)
141         c.Assert(user1.IsAdmin, check.Equals, true)
142 }
143
144 func (s *FederationSuite) TearDownSuite(c *check.C) {
145         for _, c := range s.testClusters {
146                 c.Super.Stop()
147         }
148         _ = os.Setenv("ARVADOS_API_HOST", origAPIHost)
149         _ = os.Setenv("ARVADOS_API_TOKEN", origAPIToken)
150 }
151
152 func (s *FederationSuite) TestGroupSyncingOnFederatedCluster(c *check.C) {
153         // Get admin user's V2 token
154         conn1 := s.testClusters["z1111"].Conn()
155         rootctx1, _, _ := s.testClusters["z1111"].RootClients()
156         userctx1, _, _, _ := s.testClusters["z1111"].UserClients(rootctx1, c, conn1, s.oidcprovider.AuthEmail, true)
157         user1Auth, err := conn1.APIClientAuthorizationCurrent(userctx1, arvados.GetOptions{})
158         c.Check(err, check.IsNil)
159         userV2Token := user1Auth.TokenV2()
160
161         // Get federated admin clients on z2222 to set up environment
162         conn2 := s.testClusters["z2222"].Conn()
163         userctx2, userac2, _ := s.testClusters["z2222"].ClientsWithToken(userV2Token)
164         user2, err := conn2.UserGetCurrent(userctx2, arvados.GetOptions{})
165         c.Check(err, check.IsNil)
166         c.Check(user2.IsAdmin, check.Equals, true)
167
168         // Set up environment for sync-groups using admin user credentials on z2222
169         err = os.Setenv("ARVADOS_API_HOST", userac2.APIHost)
170         c.Assert(err, check.IsNil)
171         err = os.Setenv("ARVADOS_API_TOKEN", userac2.AuthToken)
172         c.Assert(err, check.IsNil)
173
174         // Check that no parent group is created
175         gl := arvados.GroupList{}
176         params := arvados.ResourceListParams{
177                 Filters: []arvados.Filter{{
178                         Attr:     "owner_uuid",
179                         Operator: "=",
180                         Operand:  s.testClusters["z2222"].ClusterID + "-tpzed-000000000000000",
181                 }, {
182                         Attr:     "name",
183                         Operator: "=",
184                         Operand:  "Externally synchronized groups",
185                 }},
186         }
187         err = userac2.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
188         c.Assert(err, check.IsNil)
189         c.Assert(gl.ItemsAvailable, check.Equals, 0)
190
191         // Set up config, confirm that the parent group was created
192         os.Args = []string{"cmd", "somefile.csv"}
193         config, err := GetConfig()
194         c.Assert(err, check.IsNil)
195         userac2.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
196         c.Assert(gl.ItemsAvailable, check.Equals, 1)
197
198         // Run the tool with custom config
199         data := [][]string{
200                 {"TestGroup1", user2.Email},
201         }
202         tmpfile, err := MakeTempCSVFile(data)
203         c.Assert(err, check.IsNil)
204         defer os.Remove(tmpfile.Name()) // clean up
205         config.Path = tmpfile.Name()
206         err = doMain(&config)
207         c.Assert(err, check.IsNil)
208         // Check the group was created correctly, and has the user as a member
209         groupUUID, err := RemoteGroupExists(&config, "TestGroup1")
210         c.Assert(err, check.IsNil)
211         c.Assert(groupUUID, check.Not(check.Equals), "")
212         c.Assert(GroupMembershipExists(config.Client, user2.UUID, groupUUID, "can_write"), check.Equals, true)
213 }