github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/api/testing/macaroonsuite.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "net/http" 8 "net/http/cookiejar" 9 "net/url" 10 11 "github.com/juju/errors" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/names.v2" 14 "gopkg.in/macaroon-bakery.v1/bakery/checkers" 15 "gopkg.in/macaroon-bakery.v1/bakerytest" 16 "gopkg.in/macaroon-bakery.v1/httpbakery" 17 18 "github.com/juju/juju/api" 19 "github.com/juju/juju/controller" 20 jujutesting "github.com/juju/juju/juju/testing" 21 "github.com/juju/juju/testing/factory" 22 ) 23 24 // MacaroonSuite wraps a JujuConnSuite with macaroon authentication 25 // enabled. 26 type MacaroonSuite struct { 27 jujutesting.JujuConnSuite 28 29 // discharger holds the third-party discharger used 30 // for authentication. 31 discharger *bakerytest.Discharger 32 33 // DischargerLogin is called by the discharger when an 34 // API macaroon is discharged. It should either return 35 // the chosen username or an empty string, in which case 36 // the discharge is denied. 37 // If this is nil, func() {return ""} is implied. 38 DischargerLogin func() string 39 } 40 41 func (s *MacaroonSuite) SetUpTest(c *gc.C) { 42 s.discharger = bakerytest.NewDischarger(nil, func(req *http.Request, cond, arg string) ([]checkers.Caveat, error) { 43 if cond != "is-authenticated-user" { 44 return nil, errors.New("unknown caveat") 45 } 46 var username string 47 if s.DischargerLogin != nil { 48 username = s.DischargerLogin() 49 } 50 if username == "" { 51 return nil, errors.New("login denied by discharger") 52 } 53 return []checkers.Caveat{checkers.DeclaredCaveat("username", username)}, nil 54 }) 55 s.JujuConnSuite.ControllerConfigAttrs = map[string]interface{}{ 56 controller.IdentityURL: s.discharger.Location(), 57 } 58 s.JujuConnSuite.SetUpTest(c) 59 } 60 61 func (s *MacaroonSuite) TearDownTest(c *gc.C) { 62 s.discharger.Close() 63 s.JujuConnSuite.TearDownTest(c) 64 } 65 66 // AddModelUser is a convenience function that adds an external 67 // user to the current model. It will panic 68 // if the user name is local. 69 func (s *MacaroonSuite) AddModelUser(c *gc.C, username string) { 70 if names.NewUserTag(username).IsLocal() { 71 panic("cannot use MacaroonSuite.AddModelUser to add a local name") 72 } 73 s.Factory.MakeModelUser(c, &factory.ModelUserParams{ 74 User: username, 75 }) 76 } 77 78 // OpenAPI opens a connection to the API using the given information. 79 // and empty DialOpts. If info is nil, s.APIInfo(c) is used. 80 // If jar is non-nil, it will be used as the store for the cookies created 81 // as a result of API interaction. 82 func (s *MacaroonSuite) OpenAPI(c *gc.C, info *api.Info, jar http.CookieJar) api.Connection { 83 if info == nil { 84 info = s.APIInfo(c) 85 } 86 bakeryClient := httpbakery.NewClient() 87 if jar != nil { 88 bakeryClient.Client.Jar = jar 89 } 90 conn, err := api.Open(info, api.DialOpts{ 91 BakeryClient: bakeryClient, 92 }) 93 c.Assert(err, gc.IsNil) 94 return conn 95 } 96 97 // APIInfo returns API connection info suitable for 98 // connecting to the API using macaroon authentication. 99 func (s *MacaroonSuite) APIInfo(c *gc.C) *api.Info { 100 info := s.JujuConnSuite.APIInfo(c) 101 info.Tag = nil 102 info.Password = "" 103 return info 104 } 105 106 // NewClearableCookieJar returns a new ClearableCookieJar. 107 func NewClearableCookieJar() *ClearableCookieJar { 108 jar, err := cookiejar.New(nil) 109 if err != nil { 110 panic(err) 111 } 112 return &ClearableCookieJar{ 113 jar: jar, 114 } 115 } 116 117 // ClearableCookieJar implements a cookie jar 118 // that can be cleared of all cookies for testing purposes. 119 type ClearableCookieJar struct { 120 jar http.CookieJar 121 } 122 123 // Clear clears all the cookies in the jar. 124 // It is not OK to call Clear concurrently 125 // with the other methods. 126 func (jar *ClearableCookieJar) Clear() { 127 newJar, err := cookiejar.New(nil) 128 if err != nil { 129 panic(err) 130 } 131 jar.jar = newJar 132 } 133 134 // Cookies implements http.CookieJar.Cookies. 135 func (jar *ClearableCookieJar) Cookies(u *url.URL) []*http.Cookie { 136 return jar.jar.Cookies(u) 137 } 138 139 // Cookies implements http.CookieJar.SetCookies. 140 func (jar *ClearableCookieJar) SetCookies(u *url.URL, cookies []*http.Cookie) { 141 jar.jar.SetCookies(u, cookies) 142 }