21700: Install Bundler system-wide in Rails postinst
[arvados.git] / lib / controller / federation / logout_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package federation
6
7 import (
8         "context"
9         "errors"
10         "fmt"
11         "net/url"
12
13         "git.arvados.org/arvados.git/lib/ctrlctx"
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15         "git.arvados.org/arvados.git/sdk/go/arvadostest"
16         "git.arvados.org/arvados.git/sdk/go/auth"
17         check "gopkg.in/check.v1"
18 )
19
20 var _ = check.Suite(&LogoutSuite{})
21 var emptyURL = &url.URL{}
22
23 type LogoutStub struct {
24         arvadostest.APIStub
25         redirectLocation *url.URL
26 }
27
28 func (as *LogoutStub) CheckCalls(c *check.C, returnURL *url.URL) bool {
29         actual := as.APIStub.Calls(as.APIStub.Logout)
30         allOK := c.Check(actual, check.Not(check.HasLen), 0,
31                 check.Commentf("Logout stub never called"))
32         expected := returnURL.String()
33         for _, call := range actual {
34                 opts, ok := call.Options.(arvados.LogoutOptions)
35                 allOK = c.Check(ok, check.Equals, true,
36                         check.Commentf("call options were not LogoutOptions")) &&
37                         c.Check(opts.ReturnTo, check.Equals, expected) &&
38                         allOK
39         }
40         return allOK
41 }
42
43 func (as *LogoutStub) Logout(ctx context.Context, options arvados.LogoutOptions) (arvados.LogoutResponse, error) {
44         as.APIStub.Logout(ctx, options)
45         loc := as.redirectLocation.String()
46         if loc == "" {
47                 loc = options.ReturnTo
48         }
49         return arvados.LogoutResponse{
50                 RedirectLocation: loc,
51         }, as.Error
52 }
53
54 type LogoutSuite struct {
55         FederationSuite
56 }
57
58 func (s *LogoutSuite) badReturnURL(path string) *url.URL {
59         return &url.URL{
60                 Scheme: "https",
61                 Host:   "example.net",
62                 Path:   path,
63         }
64 }
65
66 func (s *LogoutSuite) goodReturnURL(path string) *url.URL {
67         u, _ := url.Parse(s.cluster.Services.Workbench2.ExternalURL.String())
68         u.Path = path
69         return u
70 }
71
72 func (s *LogoutSuite) setupFederation(loginCluster string) {
73         if loginCluster == "" {
74                 s.cluster.Login.Test.Enable = true
75         } else {
76                 s.cluster.Login.LoginCluster = loginCluster
77         }
78         dbconn := ctrlctx.DBConnector{PostgreSQL: s.cluster.PostgreSQL}
79         s.fed = New(s.ctx, s.cluster, nil, dbconn.GetDB)
80 }
81
82 func (s *LogoutSuite) setupStub(c *check.C, id string, stubURL *url.URL, stubErr error) *LogoutStub {
83         loc, err := url.Parse(stubURL.String())
84         c.Check(err, check.IsNil)
85         stub := LogoutStub{redirectLocation: loc}
86         stub.Error = stubErr
87         if id == s.cluster.ClusterID {
88                 s.fed.local = &stub
89         } else {
90                 s.addDirectRemote(c, id, &stub)
91         }
92         return &stub
93 }
94
95 func (s *LogoutSuite) v2Token(clusterID string) string {
96         return fmt.Sprintf("v2/%s-gj3su-12345abcde67890/abcdefghijklmnopqrstuvwxy", clusterID)
97 }
98
99 func (s *LogoutSuite) TestLocalLogoutOK(c *check.C) {
100         s.setupFederation("")
101         resp, err := s.fed.Logout(s.ctx, arvados.LogoutOptions{})
102         c.Check(err, check.IsNil)
103         c.Check(resp.RedirectLocation, check.Equals, s.cluster.Services.Workbench2.ExternalURL.String())
104 }
105
106 func (s *LogoutSuite) TestLocalLogoutRedirect(c *check.C) {
107         s.setupFederation("")
108         expURL := s.cluster.Services.Workbench1.ExternalURL
109         opts := arvados.LogoutOptions{ReturnTo: expURL.String()}
110         resp, err := s.fed.Logout(s.ctx, opts)
111         c.Check(err, check.IsNil)
112         c.Check(resp.RedirectLocation, check.Equals, expURL.String())
113 }
114
115 func (s *LogoutSuite) TestLocalLogoutBadRequestError(c *check.C) {
116         s.setupFederation("")
117         returnTo := s.badReturnURL("TestLocalLogoutBadRequestError")
118         opts := arvados.LogoutOptions{ReturnTo: returnTo.String()}
119         _, err := s.fed.Logout(s.ctx, opts)
120         c.Check(err, check.NotNil)
121 }
122
123 func (s *LogoutSuite) TestRemoteLogoutRedirect(c *check.C) {
124         s.setupFederation("zhome")
125         redirect := url.URL{Scheme: "https", Host: "example.com"}
126         loginStub := s.setupStub(c, "zhome", &redirect, nil)
127         returnTo := s.goodReturnURL("TestRemoteLogoutRedirect")
128         resp, err := s.fed.Logout(s.ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
129         c.Check(err, check.IsNil)
130         c.Check(resp.RedirectLocation, check.Equals, redirect.String())
131         loginStub.CheckCalls(c, returnTo)
132 }
133
134 func (s *LogoutSuite) TestRemoteLogoutError(c *check.C) {
135         s.setupFederation("zhome")
136         expErr := errors.New("TestRemoteLogoutError expErr")
137         loginStub := s.setupStub(c, "zhome", emptyURL, expErr)
138         returnTo := s.goodReturnURL("TestRemoteLogoutError")
139         _, err := s.fed.Logout(s.ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
140         c.Check(err, check.Equals, expErr)
141         loginStub.CheckCalls(c, returnTo)
142 }
143
144 func (s *LogoutSuite) TestRemoteLogoutLocalRedirect(c *check.C) {
145         s.setupFederation("zhome")
146         loginStub := s.setupStub(c, "zhome", emptyURL, nil)
147         redirect := url.URL{Scheme: "https", Host: "example.com"}
148         localStub := s.setupStub(c, "aaaaa", &redirect, nil)
149         resp, err := s.fed.Logout(s.ctx, arvados.LogoutOptions{})
150         c.Check(err, check.IsNil)
151         c.Check(resp.RedirectLocation, check.Equals, redirect.String())
152         // emptyURL to match the empty LogoutOptions
153         loginStub.CheckCalls(c, emptyURL)
154         localStub.CheckCalls(c, emptyURL)
155 }
156
157 func (s *LogoutSuite) TestRemoteLogoutLocalError(c *check.C) {
158         s.setupFederation("zhome")
159         expErr := errors.New("TestRemoteLogoutLocalError expErr")
160         loginStub := s.setupStub(c, "zhome", emptyURL, nil)
161         localStub := s.setupStub(c, "aaaaa", emptyURL, expErr)
162         _, err := s.fed.Logout(s.ctx, arvados.LogoutOptions{})
163         c.Check(err, check.Equals, expErr)
164         loginStub.CheckCalls(c, emptyURL)
165         localStub.CheckCalls(c, emptyURL)
166 }
167
168 func (s *LogoutSuite) TestV2TokenRedirect(c *check.C) {
169         s.setupFederation("")
170         redirect := url.URL{Scheme: "https", Host: "example.com"}
171         returnTo := s.goodReturnURL("TestV2TokenRedirect")
172         localErr := errors.New("TestV2TokenRedirect error")
173         tokenStub := s.setupStub(c, "zzzzz", &redirect, nil)
174         s.setupStub(c, "aaaaa", emptyURL, localErr)
175         tokens := []string{s.v2Token("zzzzz")}
176         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
177         resp, err := s.fed.Logout(ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
178         c.Check(err, check.IsNil)
179         c.Check(resp.RedirectLocation, check.Equals, redirect.String())
180         tokenStub.CheckCalls(c, returnTo)
181 }
182
183 func (s *LogoutSuite) TestV2TokenError(c *check.C) {
184         s.setupFederation("")
185         returnTo := s.goodReturnURL("TestV2TokenError")
186         tokenErr := errors.New("TestV2TokenError error")
187         tokenStub := s.setupStub(c, "zzzzz", emptyURL, tokenErr)
188         s.setupStub(c, "aaaaa", emptyURL, nil)
189         tokens := []string{s.v2Token("zzzzz")}
190         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
191         _, err := s.fed.Logout(ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
192         c.Check(err, check.Equals, tokenErr)
193         tokenStub.CheckCalls(c, returnTo)
194 }
195
196 func (s *LogoutSuite) TestV2TokenLocalRedirect(c *check.C) {
197         s.setupFederation("")
198         redirect := url.URL{Scheme: "https", Host: "example.com"}
199         tokenStub := s.setupStub(c, "zzzzz", emptyURL, nil)
200         localStub := s.setupStub(c, "aaaaa", &redirect, nil)
201         tokens := []string{s.v2Token("zzzzz")}
202         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
203         resp, err := s.fed.Logout(ctx, arvados.LogoutOptions{})
204         c.Check(err, check.IsNil)
205         c.Check(resp.RedirectLocation, check.Equals, redirect.String())
206         tokenStub.CheckCalls(c, emptyURL)
207         localStub.CheckCalls(c, emptyURL)
208 }
209
210 func (s *LogoutSuite) TestV2TokenLocalError(c *check.C) {
211         s.setupFederation("")
212         tokenErr := errors.New("TestV2TokenLocalError error")
213         tokenStub := s.setupStub(c, "zzzzz", emptyURL, nil)
214         localStub := s.setupStub(c, "aaaaa", emptyURL, tokenErr)
215         tokens := []string{s.v2Token("zzzzz")}
216         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
217         _, err := s.fed.Logout(ctx, arvados.LogoutOptions{})
218         c.Check(err, check.Equals, tokenErr)
219         tokenStub.CheckCalls(c, emptyURL)
220         localStub.CheckCalls(c, emptyURL)
221 }
222
223 func (s *LogoutSuite) TestV2LocalTokenRedirect(c *check.C) {
224         s.setupFederation("")
225         redirect := url.URL{Scheme: "https", Host: "example.com"}
226         returnTo := s.goodReturnURL("TestV2LocalTokenRedirect")
227         localStub := s.setupStub(c, "aaaaa", &redirect, nil)
228         tokens := []string{s.v2Token("aaaaa")}
229         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
230         resp, err := s.fed.Logout(ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
231         c.Check(err, check.IsNil)
232         c.Check(resp.RedirectLocation, check.Equals, redirect.String())
233         localStub.CheckCalls(c, returnTo)
234 }
235
236 func (s *LogoutSuite) TestV2LocalTokenError(c *check.C) {
237         s.setupFederation("")
238         returnTo := s.goodReturnURL("TestV2LocalTokenError")
239         tokenErr := errors.New("TestV2LocalTokenError error")
240         localStub := s.setupStub(c, "aaaaa", emptyURL, tokenErr)
241         tokens := []string{s.v2Token("aaaaa")}
242         ctx := auth.NewContext(s.ctx, &auth.Credentials{Tokens: tokens})
243         _, err := s.fed.Logout(ctx, arvados.LogoutOptions{ReturnTo: returnTo.String()})
244         c.Check(err, check.Equals, tokenErr)
245         localStub.CheckCalls(c, returnTo)
246 }