1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
|
// Copyright 2016 Canonical Ltd.
// Licensed under the LGPLv3, see LICENSE file for details.
package openid_test
import (
"errors"
"net/url"
"testing"
qt "github.com/frankban/quicktest"
yopenid "github.com/yohcop/openid-go"
"gopkg.in/errgo.v1"
macaroon "gopkg.in/macaroon.v2"
"github.com/juju/usso"
"github.com/juju/usso/openid"
)
type openidSuite struct {
}
var redirectURLTests = []struct {
about string
server usso.UbuntuSSOServer
request *openid.Request
expect string
}{{
about: "production-with-only-return_to",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to",
}, {
about: "staging-with-only-return_to",
server: usso.StagingUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
},
expect: "https://login.staging.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to",
}, {
about: "with-realm",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to/abcdef",
Realm: "http://return.to",
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to/abcdef&openid.realm=http://return.to",
}, {
about: "with-teams",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
Teams: []string{"team1", "team2"},
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to&openid.ns.lp=http://ns.launchpad.net/2007/openid-teams&openid.lp.query_membership=team1,team2",
}, {
about: "with sreg.required",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
SRegRequired: []string{openid.SRegEmail, openid.SRegProvince},
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.required=email,x_province",
}, {
about: "with-sreg.optional",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
SRegOptional: []string{openid.SRegNickname, openid.SRegCity},
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.optional=nickname,x_city",
}, {
about: "with-sreg",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
SRegRequired: []string{openid.SRegEmail, openid.SRegProvince},
SRegOptional: []string{openid.SRegNickname, openid.SRegCity},
},
expect: "https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.required=email,x_province&openid.sreg.optional=nickname,x_city",
}, {
about: "with-caveat",
server: usso.ProductionUbuntuSSOServer,
request: &openid.Request{
ReturnTo: "http://return.to",
CaveatID: `{"secret": "squirrel", "version": 1}`,
},
expect: `https://login.ubuntu.com/+openid?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=checkid_setup&openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.return_to=http://return.to&openid.ns.macaroon=http://ns.login.ubuntu.com/2016/openid-macaroon&openid.macaroon.caveat_id={"secret": "squirrel", "version": 1}`,
}}
func TestRedirectURL(t *testing.T) {
c := qt.New(t)
for _, test := range redirectURLTests {
c.Run(test.about, func(c *qt.C) {
client := openid.NewClient(test.server, nil, nil)
u, err := url.Parse(client.RedirectURL(test.request))
c.Assert(err, qt.Equals, nil)
expectURL, err := url.Parse(test.expect)
c.Assert(err, qt.Equals, nil)
query := u.Query()
expectQuery := expectURL.Query()
c.Assert(query, qt.DeepEquals, expectQuery)
u.RawQuery = ""
expectURL.RawQuery = ""
c.Assert(u, qt.DeepEquals, expectURL)
})
}
}
var verifyTests = []struct {
about string
url string
server usso.UbuntuSSOServer
nonceStore yopenid.NonceStore
discoveryCache yopenid.DiscoveryCache
verifyF func(*qt.C, string, yopenid.DiscoveryCache, yopenid.NonceStore) (string, error)
expectResponse *openid.Response
expectError string
expectErrorCause error
}{{
about: "id only",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
},
}, {
about: "teams",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity,lp.is_member&openid.sig=AAAA&openid.ns.lp=http://ns.launchpad.net/2007/openid-teams&openid.lp.is_member=team1,team2",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
Teams: []string{"team1", "team2"},
},
}, {
about: "simple-registration",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity,sreg.email,sreg.fullname&openid.sig=AAAA&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.email=a@example.org&openid.sreg.fullname=A",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
SReg: map[string]string{
openid.SRegEmail: "a@example.org",
openid.SRegFullName: "A",
},
},
}, {
about: "teams-not-signed",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA&openid.ns.lp=http://ns.launchpad.net/2007/openid-teams&openid.lp.is_member=team1,team2",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
},
}, {
about: "simple-registration-not-signed",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA&openid.ns.sreg=http://openid.net/extensions/sreg/1.1&openid.sreg.email=a@example.org&openid.sreg.fullname=A",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
},
}, {
about: "bad-url",
url: "://return.to",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectError: "parse ://return.to: missing protocol scheme",
}, {
about: "unexpected-OP",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA",
server: usso.StagingUbuntuSSOServer,
verifyF: verifySuccess,
expectError: `OpenID response from unexpected endpoint "https://login.ubuntu.com/\+openid"`,
}, {
about: "verification-failure",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA",
server: usso.ProductionUbuntuSSOServer,
verifyF: func(*qt.C, string, yopenid.DiscoveryCache, yopenid.NonceStore) (string, error) {
return "", errors.New("TEST!")
},
expectError: `TEST!`,
}, {
about: "uses-specified-NonceStore",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA",
server: usso.ProductionUbuntuSSOServer,
nonceStore: testNonceStore,
verifyF: func(c *qt.C, _ string, _ yopenid.DiscoveryCache, ns yopenid.NonceStore) (string, error) {
c.Assert(ns, qt.Equals, testNonceStore)
return "PASS", nil
},
expectResponse: &openid.Response{
ID: "PASS",
},
}, {
about: "creates-server-specific-DiscoveryCache",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity&openid.sig=AAAA",
server: usso.ProductionUbuntuSSOServer,
verifyF: func(c *qt.C, _ string, dc yopenid.DiscoveryCache, _ yopenid.NonceStore) (string, error) {
c.Assert(dc, qt.Not(qt.IsNil))
di := dc.Get("https://login.ubuntu.com/+id/AAAAAA")
c.Assert(di, qt.Not(qt.IsNil))
c.Assert(di.ClaimedID(), qt.Equals, "https://login.ubuntu.com/+id/AAAAAA")
c.Assert(di.OpLocalID(), qt.Equals, "https://login.ubuntu.com/+id/AAAAAA")
c.Assert(di.OpEndpoint(), qt.Equals, "https://login.ubuntu.com/+openid")
di = dc.Get("https://login.staging.ubuntu.com/+id/AAAAAA")
c.Assert(di, qt.IsNil)
return "PASS", nil
},
expectResponse: &openid.Response{
ID: "PASS",
},
}, {
about: "cancel-response",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=cancel",
verifyF: func(c *qt.C, _ string, _ yopenid.DiscoveryCache, _ yopenid.NonceStore) (string, error) {
c.Fatalf("verify should not have been called")
panic("unreachable")
},
expectError: "login cancelled",
expectErrorCause: openid.ErrCancel,
}, {
about: "bad-mode",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=bad",
verifyF: func(c *qt.C, _ string, _ yopenid.DiscoveryCache, _ yopenid.NonceStore) (string, error) {
c.Fatalf("verify should not have been called")
panic("unreachable")
},
expectError: `unrecognised mode "bad"`,
}, {
about: "openid-error",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=error&openid.error=test+message&openid.contact=test+contact&openid.reference=test+reference",
verifyF: func(c *qt.C, _ string, _ yopenid.DiscoveryCache, _ yopenid.NonceStore) (string, error) {
c.Fatalf("verify should not have been called")
panic("unreachable")
},
expectError: `test message`,
expectErrorCause: &openid.OpenIDError{
Message: "test message",
Contact: "test contact",
Reference: "test reference",
},
}, {
about: "discharge",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity,macaroon.discharge&openid.sig=AAAA&openid.ns.macaroon=http://ns.login.ubuntu.com/2016/openid-macaroon&openid.macaroon.discharge=MDAwZWxvY2F0aW9uIAowMDE0aWRlbnRpZmllciBUZXN0CjAwMmZzaWduYXR1cmUgHF1XmyS2hpzHuObgmCBWFzpF7U0hFRuLnDsfkGLG9kAK",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
Discharge: testMacaroon,
},
}, {
about: "discharge-bad-base64",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity,macaroon.discharge&openid.sig=AAAA&openid.ns.macaroon=http://ns.login.ubuntu.com/2016/openid-macaroon&openid.macaroon.discharge=MDAwZWxvY2F0aW9uIAowMDE0aWRlbnRpZmllciBUZXN0CjAwMmZzaWduYXR1cmUgHF1XmyS2hpzHuObgmCBWFzpF7U0hFRuLnDsfkGLG9kA:",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
},
}, {
about: "discharge-invalid-macaroon",
url: "http://return.to?openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://login.ubuntu.com/%2Bopenid&openid.claimed_id=https://login.ubuntu.com/%2Bid/AAAAAA&openid.identity=https://login.ubuntu.com/%2Bid/AAAAAA&openid.return_to=http://return.to&openid.response_nonce=2005-05-15T17:11:51ZUNIQUE&openid.assoc_handle=1&openid.signed=op_endpoint,return_to,response_nonce,assoc_handle,claimed_id,identity,macaroon.discharge&openid.sig=AAAA&openid.ns.macaroon=http://ns.login.ubuntu.com/2016/openid-macaroon&openid.macaroon.discharge=NDAwZWxvY2F0aW9uIAowMDE0aWRlbnRpZmllciBUZXN0CjAwMmZzaWduYXR1cmUgHF1XmyS2hpzHuObgmCBWFzpF7U0hFRuLnDsfkGLG9kAK",
server: usso.ProductionUbuntuSSOServer,
verifyF: verifySuccess,
expectResponse: &openid.Response{
ID: "https://login.ubuntu.com/+id/AAAAAA",
},
}}
var testMacaroon = func() *macaroon.Macaroon {
m, err := macaroon.New([]byte("A"), []byte("Test"), "", macaroon.V1)
if err != nil {
panic(err)
}
return m
}()
func TestVerify(t *testing.T) {
c := qt.New(t)
for _, test := range verifyTests {
c.Run(test.about, func(c *qt.C) {
*openid.Verify = func(s string, dc yopenid.DiscoveryCache, ns yopenid.NonceStore) (string, error) {
return test.verifyF(c, s, dc, ns)
}
client := openid.NewClient(test.server, test.nonceStore, test.discoveryCache)
r, err := client.Verify(test.url)
if test.expectError != "" {
c.Assert(err, qt.ErrorMatches, test.expectError)
if test.expectErrorCause != nil {
c.Assert(errgo.Cause(err), qt.DeepEquals, test.expectErrorCause)
}
return
}
c.Assert(err, qt.Equals, nil)
c.Assert(r, qt.DeepEquals, test.expectResponse)
})
}
}
func verifySuccess(_ *qt.C, s string, _ yopenid.DiscoveryCache, _ yopenid.NonceStore) (string, error) {
u, err := url.Parse(s)
if err != nil {
return "", err
}
return u.Query().Get("openid.claimed_id"), nil
}
var testNonceStore = yopenid.NewSimpleNonceStore()
|