{"action":"created","comment":{"url":"https://api.github.com/repos/juju/usso/pulls/comments/60044792","id":60044792,"diff_hunk":"@@ -0,0 +1,273 @@\n+// Copyright 2016 Canonical Ltd.\n+// Licensed under the LGPLv3, see LICENSE file for details.\n+\n+// Package openid contains functions to help log-in to Ubuntu SSO using\n+// OpenID 2.0.\n+package openid\n+\n+import (\n+\t\"net/url\"\n+\t\"strings\"\n+\n+\t\"github.com/yohcop/openid-go\"\n+\t\"gopkg.in/errgo.v1\"\n+\n+\t\"github.com/juju/usso\"\n+)\n+\n+const (\n+\t// These standard simple registration fields are supported by\n+\t// Ubuntu SSO.\n+\tSRegNickname = \"nickname\"\n+\tSRegEmail    = \"email\"\n+\tSRegFullName = \"fullname\"\n+\tSRegPostcode = \"postcode\"\n+\tSRegCountry  = \"country\"\n+\tSRegLanguage = \"language\"\n+\tSRegTimezone = \"timezone\"\n+\n+\t// These non-standard simple registration fields are supported by\n+\t// Ubuntu SSO.\n+\tSRegAddress1 = \"x_address1\"\n+\tSRegAddress2 = \"x_address2\"\n+\tSRegCity     = \"x_city\"\n+\tSRegProvince = \"x_province\"\n+\tSRegPhone    = \"x_phone\"\n+)\n+\n+const (\n+\tnsSReg  = \"http://openid.net/extensions/sreg/1.1\"\n+\tnsTeams = \"http://ns.launchpad.net/2007/openid-teams\"\n+)\n+\n+var (\n+\tErrCancel = errgo.New(\"login cancelled\")\n+\tErrOpenID = errgo.New(\"OpenID error\")\n+)\n+\n+// NonceStore is the NonceStore type from github.com/yohcop/openid-go. It\n+// is replicated here for the convenience of clients.\n+type NonceStore interface {\n+\topenid.NonceStore\n+}\n+\n+// defaultNonceStore is the NonceStore to use when there is none specified.\n+var defaultNonceStore = openid.NewSimpleNonceStore()\n+\n+// DiscoveryCache is the DiscoveryCache type from\n+// github.com/yohcop/openid-go. It is replicated here for the convenience\n+// of clients.\n+type DiscoveryCache interface {\n+\topenid.DiscoveryCache\n+}\n+\n+// Client is an OpenID client that provides OpenID login for a specific\n+// Ubuntu SSO server.\n+type Client struct {\n+\t// Server holds the Ubuntu SSO server that OpenID requests will\n+\t// be made against. If this is not set then\n+\t// usso.ProductionUbuntuSSOServer will be used.\n+\tServer usso.UbuntuSSOServer\n+\n+\t// NonceStore contains the NonceStore used to verify the OpenID\n+\t// responses have not been previously processed. If this is nil\n+\t// an in-memory NonceStore will be used.\n+\tNonceStore NonceStore\n+\n+\t// DiscoveryCache contains a DiscoveryCache to use when verifying\n+\t// OpenID responses. If this is nil then a DiscoveryCache derived\n+\t// from Server will be used.\n+\tDiscoveryCache DiscoveryCache\n+}\n+\n+// Request contains the paramaters for an UbuntuSSO OpenID login request.\n+type Request struct {\n+\t// ReturnTo contains the callback address for the service, this is\n+\t// where the login response will come.\n+\tReturnTo string\n+\n+\t// Realm contains the realm that the user is logging into. See\n+\t// http://openid.net/specs/openid-authentication-2_0.html#realms\n+\t// for details.\n+\tRealm string\n+\n+\t// Teams contains a list of launchpad teams to query membership\n+\t// of for the logged in user.\n+\tTeams []string\n+\n+\t// SRegRequired contains a list of simple registration fields\n+\t// that are required by the service.\n+\tSRegRequired []string\n+\n+\t// SRegOptional contains a list of simple registration fields\n+\t// that are optional, but requested by the service.\n+\tSRegOptional []string\n+}\n+\n+// RedirectURL creates an OpenID login request addressed to c.Server.\n+func (c Client) RedirectURL(r *Request) string {\n+\tv := url.Values{\n+\t\t\"openid.ns\":         {\"http://specs.openid.net/auth/2.0\"},\n+\t\t\"openid.mode\":       {\"checkid_setup\"},\n+\t\t\"openid.claimed_id\": {\"http://specs.openid.net/auth/2.0/identifier_select\"},\n+\t\t\"openid.identity\":   {\"http://specs.openid.net/auth/2.0/identifier_select\"},\n+\t\t\"openid.return_to\":  {r.ReturnTo},\n+\t}\n+\tif r.Realm != \"\" {\n+\t\tv.Set(\"openid.realm\", r.Realm)\n+\t}\n+\tif len(r.Teams) > 0 {\n+\t\tv.Set(\"openid.ns.lp\", nsTeams)\n+\t\tv.Set(\"openid.lp.query_membership\", strings.Join(r.Teams, \",\"))\n+\t}\n+\tif len(r.SRegRequired) > 0 {\n+\t\tv.Set(\"openid.ns.sreg\", nsSReg)\n+\t\tv.Set(\"openid.sreg.required\", strings.Join(r.SRegRequired, \",\"))\n+\t}\n+\tif len(r.SRegOptional) > 0 {\n+\t\tv.Set(\"openid.ns.sreg\", nsSReg)\n+\t\tv.Set(\"openid.sreg.optional\", strings.Join(r.SRegOptional, \",\"))\n+\t}\n+\tif (c.Server == usso.UbuntuSSOServer{}) {\n+\t\tc.Server = usso.ProductionUbuntuSSOServer\n+\t}\n+\treturn c.Server.OpenIDURL() + \"?\" + v.Encode()\n+}\n+\n+// Response contains the values returned from Ubuntu SSO after a\n+// successful login.\n+type Response struct {\n+\t// ID contians the claimed_id of the logged in user. This will\n+\t// always be present in a successful login.\n+\tID string\n+\n+\t// Teams contains any launchpad teams that were specified in the\n+\t// OpenID response.\n+\tTeams []string\n+\n+\t// SReg contains any simple registration fileds are\n+\t// were provided in the OpenID response.\n+\tSReg map[string]string\n+}\n+\n+// verify is used to perform the OpenID verification of the login\n+// response. This is declared as a variable so it can be overridden for\n+// testing.\n+var verify = openid.Verify\n+\n+// Verify processes a positive assertion from Ubuntu SSO. If the\n+// verification is successful any parameters asserted by Ubuntu SSO will\n+// be set in the Response. If the OpenID response reports that the login\n+// was cancelled then an error will be returned with a cause of\n+// ErrCancel. If the OpenID response reports an error occured then an\n+// err will be returned with a cause fo ErrOpenID.","path":"openid/openid.go","position":163,"original_position":163,"commit_id":"71e55c01395f8367c298fe268b52aa8931c3ab21","original_commit_id":"71e55c01395f8367c298fe268b52aa8931c3ab21","user":{"login":"mhilton","id":1136018,"avatar_url":"https://avatars.githubusercontent.com/u/1136018?v=3","gravatar_id":"","url":"https://api.github.com/users/mhilton","html_url":"https://github.com/mhilton","followers_url":"https://api.github.com/users/mhilton/followers","following_url":"https://api.github.com/users/mhilton/following{/other_user}","gists_url":"https://api.github.com/users/mhilton/gists{/gist_id}","starred_url":"https://api.github.com/users/mhilton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhilton/subscriptions","organizations_url":"https://api.github.com/users/mhilton/orgs","repos_url":"https://api.github.com/users/mhilton/repos","events_url":"https://api.github.com/users/mhilton/events{/privacy}","received_events_url":"https://api.github.com/users/mhilton/received_events","type":"User","site_admin":false},"body":"This bit has been reworded so done.","created_at":"2016-04-18T11:52:20Z","updated_at":"2016-04-18T11:52:20Z","html_url":"https://github.com/juju/usso/pull/6#discussion_r60044792","pull_request_url":"https://api.github.com/repos/juju/usso/pulls/6","_links":{"self":{"href":"https://api.github.com/repos/juju/usso/pulls/comments/60044792"},"html":{"href":"https://github.com/juju/usso/pull/6#discussion_r60044792"},"pull_request":{"href":"https://api.github.com/repos/juju/usso/pulls/6"}}},"pull_request":{"url":"https://api.github.com/repos/juju/usso/pulls/6","id":65305620,"html_url":"https://github.com/juju/usso/pull/6","diff_url":"https://github.com/juju/usso/pull/6.diff","patch_url":"https://github.com/juju/usso/pull/6.patch","issue_url":"https://api.github.com/repos/juju/usso/issues/6","number":6,"state":"open","locked":false,"title":"Add OpenID package","user":{"login":"mhilton","id":1136018,"avatar_url":"https://avatars.githubusercontent.com/u/1136018?v=3","gravatar_id":"","url":"https://api.github.com/users/mhilton","html_url":"https://github.com/mhilton","followers_url":"https://api.github.com/users/mhilton/followers","following_url":"https://api.github.com/users/mhilton/following{/other_user}","gists_url":"https://api.github.com/users/mhilton/gists{/gist_id}","starred_url":"https://api.github.com/users/mhilton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhilton/subscriptions","organizations_url":"https://api.github.com/users/mhilton/orgs","repos_url":"https://api.github.com/users/mhilton/repos","events_url":"https://api.github.com/users/mhilton/events{/privacy}","received_events_url":"https://api.github.com/users/mhilton/received_events","type":"User","site_admin":false},"body":"Add a package to help with performing OpenID logins against Ubuntu SSO.","created_at":"2016-04-05T13:38:48Z","updated_at":"2016-04-18T11:52:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e6bc1c1630a14e40c113dbe6e61a0f218e63bee4","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/juju/usso/pulls/6/commits","review_comments_url":"https://api.github.com/repos/juju/usso/pulls/6/comments","review_comment_url":"https://api.github.com/repos/juju/usso/pulls/comments{/number}","comments_url":"https://api.github.com/repos/juju/usso/issues/6/comments","statuses_url":"https://api.github.com/repos/juju/usso/statuses/71e55c01395f8367c298fe268b52aa8931c3ab21","head":{"label":"mhilton:000-openid","ref":"000-openid","sha":"71e55c01395f8367c298fe268b52aa8931c3ab21","user":{"login":"mhilton","id":1136018,"avatar_url":"https://avatars.githubusercontent.com/u/1136018?v=3","gravatar_id":"","url":"https://api.github.com/users/mhilton","html_url":"https://github.com/mhilton","followers_url":"https://api.github.com/users/mhilton/followers","following_url":"https://api.github.com/users/mhilton/following{/other_user}","gists_url":"https://api.github.com/users/mhilton/gists{/gist_id}","starred_url":"https://api.github.com/users/mhilton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhilton/subscriptions","organizations_url":"https://api.github.com/users/mhilton/orgs","repos_url":"https://api.github.com/users/mhilton/repos","events_url":"https://api.github.com/users/mhilton/events{/privacy}","received_events_url":"https://api.github.com/users/mhilton/received_events","type":"User","site_admin":false},"repo":{"id":55218312,"name":"juju-usso","full_name":"mhilton/juju-usso","owner":{"login":"mhilton","id":1136018,"avatar_url":"https://avatars.githubusercontent.com/u/1136018?v=3","gravatar_id":"","url":"https://api.github.com/users/mhilton","html_url":"https://github.com/mhilton","followers_url":"https://api.github.com/users/mhilton/followers","following_url":"https://api.github.com/users/mhilton/following{/other_user}","gists_url":"https://api.github.com/users/mhilton/gists{/gist_id}","starred_url":"https://api.github.com/users/mhilton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mhilton/subscriptions","organizations_url":"https://api.github.com/users/mhilton/orgs","repos_url":"https://api.github.com/users/mhilton/repos","events_url":"https://api.github.com/users/mhilton/events{/privacy}","received_events_url":"https://api.github.com/users/mhilton/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/mhilton/juju-usso","description":"","fork":true,"url":"https://api.github.com/repos/mhilton/juju-usso","forks_url":"https://api.github.com/repos/mhilton/juju-usso/forks","keys_url":"https://api.github.com/repos/mhilton/juju-usso/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mhilton/juju-usso/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mhilton/juju-usso/teams","hooks_url":"https://api.github.com/repos/mhilton/juju-usso/hooks","issue_events_url":"https://api.github.com/repos/mhilton/juju-usso/issues/events{/number}","events_url":"https://api.github.com/repos/mhilton/juju-usso/events","assignees_url":"https://api.github.com/repos/mhilton/juju-usso/assignees{/user}","branches_url":"https://api.github.com/repos/mhilton/juju-usso/branches{/branch}","tags_url":"https://api.github.com/repos/mhilton/juju-usso/tags","blobs_url":"https://api.github.com/repos/mhilton/juju-usso/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mhilton/juju-usso/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mhilton/juju-usso/git/refs{/sha}","trees_url":"https://api.github.com/repos/mhilton/juju-usso/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mhilton/juju-usso/statuses/{sha}","languages_url":"https://api.github.com/repos/mhilton/juju-usso/languages","stargazers_url":"https://api.github.com/repos/mhilton/juju-usso/stargazers","contributors_url":"https://api.github.com/repos/mhilton/juju-usso/contributors","subscribers_url":"https://api.github.com/repos/mhilton/juju-usso/subscribers","subscription_url":"https://api.github.com/repos/mhilton/juju-usso/subscription","commits_url":"https://api.github.com/repos/mhilton/juju-usso/commits{/sha}","git_commits_url":"https://api.github.com/repos/mhilton/juju-usso/git/commits{/sha}","comments_url":"https://api.github.com/repos/mhilton/juju-usso/comments{/number}","issue_comment_url":"https://api.github.com/repos/mhilton/juju-usso/issues/comments{/number}","contents_url":"https://api.github.com/repos/mhilton/juju-usso/contents/{+path}","compare_url":"https://api.github.com/repos/mhilton/juju-usso/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mhilton/juju-usso/merges","archive_url":"https://api.github.com/repos/mhilton/juju-usso/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mhilton/juju-usso/downloads","issues_url":"https://api.github.com/repos/mhilton/juju-usso/issues{/number}","pulls_url":"https://api.github.com/repos/mhilton/juju-usso/pulls{/number}","milestones_url":"https://api.github.com/repos/mhilton/juju-usso/milestones{/number}","notifications_url":"https://api.github.com/repos/mhilton/juju-usso/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mhilton/juju-usso/labels{/name}","releases_url":"https://api.github.com/repos/mhilton/juju-usso/releases{/id}","deployments_url":"https://api.github.com/repos/mhilton/juju-usso/deployments","created_at":"2016-04-01T09:02:04Z","updated_at":"2016-04-01T09:02:18Z","pushed_at":"2016-04-18T10:58:01Z","git_url":"git://github.com/mhilton/juju-usso.git","ssh_url":"git@github.com:mhilton/juju-usso.git","clone_url":"https://github.com/mhilton/juju-usso.git","svn_url":"https://github.com/mhilton/juju-usso","homepage":null,"size":185,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"juju:master","ref":"master","sha":"68a59c96c178fbbad65926e7f93db50a2cd14f33","user":{"login":"juju","id":4604548,"avatar_url":"https://avatars.githubusercontent.com/u/4604548?v=3","gravatar_id":"","url":"https://api.github.com/users/juju","html_url":"https://github.com/juju","followers_url":"https://api.github.com/users/juju/followers","following_url":"https://api.github.com/users/juju/following{/other_user}","gists_url":"https://api.github.com/users/juju/gists{/gist_id}","starred_url":"https://api.github.com/users/juju/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/juju/subscriptions","organizations_url":"https://api.github.com/users/juju/orgs","repos_url":"https://api.github.com/users/juju/repos","events_url":"https://api.github.com/users/juju/events{/privacy}","received_events_url":"https://api.github.com/users/juju/received_events","type":"Organization","site_admin":false},"repo":{"id":39559013,"name":"usso","full_name":"juju/usso","owner":{"login":"juju","id":4604548,"avatar_url":"https://avatars.githubusercontent.com/u/4604548?v=3","gravatar_id":"","url":"https://api.github.com/users/juju","html_url":"https://github.com/juju","followers_url":"https://api.github.com/users/juju/followers","following_url":"https://api.github.com/users/juju/following{/other_user}","gists_url":"https://api.github.com/users/juju/gists{/gist_id}","starred_url":"https://api.github.com/users/juju/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/juju/subscriptions","organizations_url":"https://api.github.com/users/juju/orgs","repos_url":"https://api.github.com/users/juju/repos","events_url":"https://api.github.com/users/juju/events{/privacy}","received_events_url":"https://api.github.com/users/juju/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/juju/usso","description":"","fork":false,"url":"https://api.github.com/repos/juju/usso","forks_url":"https://api.github.com/repos/juju/usso/forks","keys_url":"https://api.github.com/repos/juju/usso/keys{/key_id}","collaborators_url":"https://api.github.com/repos/juju/usso/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/juju/usso/teams","hooks_url":"https://api.github.com/repos/juju/usso/hooks","issue_events_url":"https://api.github.com/repos/juju/usso/issues/events{/number}","events_url":"https://api.github.com/repos/juju/usso/events","assignees_url":"https://api.github.com/repos/juju/usso/assignees{/user}","branches_url":"https://api.github.com/repos/juju/usso/branches{/branch}","tags_url":"https://api.github.com/repos/juju/usso/tags","blobs_url":"https://api.github.com/repos/juju/usso/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/juju/usso/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/juju/usso/git/refs{/sha}","trees_url":"https://api.github.com/repos/juju/usso/git/trees{/sha}","statuses_url":"https://api.github.com/repos/juju/usso/statuses/{sha}","languages_url":"https://api.github.com/repos/juju/usso/languages","stargazers_url":"https://api.github.com/repos/juju/usso/stargazers","contributors_url":"https://api.github.com/repos/juju/usso/contributors","subscribers_url":"https://api.github.com/repos/juju/usso/subscribers","subscription_url":"https://api.github.com/repos/juju/usso/subscription","commits_url":"https://api.github.com/repos/juju/usso/commits{/sha}","git_commits_url":"https://api.github.com/repos/juju/usso/git/commits{/sha}","comments_url":"https://api.github.com/repos/juju/usso/comments{/number}","issue_comment_url":"https://api.github.com/repos/juju/usso/issues/comments{/number}","contents_url":"https://api.github.com/repos/juju/usso/contents/{+path}","compare_url":"https://api.github.com/repos/juju/usso/compare/{base}...{head}","merges_url":"https://api.github.com/repos/juju/usso/merges","archive_url":"https://api.github.com/repos/juju/usso/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/juju/usso/downloads","issues_url":"https://api.github.com/repos/juju/usso/issues{/number}","pulls_url":"https://api.github.com/repos/juju/usso/pulls{/number}","milestones_url":"https://api.github.com/repos/juju/usso/milestones{/number}","notifications_url":"https://api.github.com/repos/juju/usso/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/juju/usso/labels{/name}","releases_url":"https://api.github.com/repos/juju/usso/releases{/id}","deployments_url":"https://api.github.com/repos/juju/usso/deployments","created_at":"2015-07-23T09:42:43Z","updated_at":"2016-03-01T15:03:20Z","pushed_at":"2016-04-18T10:58:01Z","git_url":"git://github.com/juju/usso.git","ssh_url":"git@github.com:juju/usso.git","clone_url":"https://github.com/juju/usso.git","svn_url":"https://github.com/juju/usso","homepage":null,"size":176,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/juju/usso/pulls/6"},"html":{"href":"https://github.com/juju/usso/pull/6"},"issue":{"href":"https://api.github.com/repos/juju/usso/issues/6"},"comments":{"href":"https://api.github.com/repos/juju/usso/issues/6/comments"},"review_comments":{"href":"https://api.github.com/repos/juju/usso/pulls/6/comments"},"review_comment":{"href":"https://api.github.com/repos/juju/usso/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/juju/usso/pulls/6/commits"},"statuses":{"href":"https://api.github.com/repos/juju/usso/statuses/71e55c01395f8367c298fe268b52aa8931c3ab21"}}}}