Bugfixes and tests for getting grants.
Add tests for the grant confirmation part of the request to get a grant code.
When the request is denied, the redirect should have an access_denied error.
When the request is approved, the redirect should contain a code.
Fix numerous bugs in which the redirect URL didn't contain the parameters we
thought it would. Basically, anything that still used req.URL.Query().Set()
instead of copying the query, modifying it, and setting req.URL.RawQuery.
Fix a bug in which the redirectURL wasn't being properly set. Basically, when we
moved the redirectURI processing to the top of the file (c29c7df35905 for those
who forgot), we didn't update the reference to it lower in the file, where
redirectURI was being updated and we expected that to be reflected in the
processing.
The HTTP handler for getting grant codes is now completely tested except for
returning internal errors, which requires a new test harness be built to provoke
internal errors on demand. At this point, however, I'd like to continue on
implementing endpoints.
9 "code.secondbit.org/uuid"
12 // Context wraps the different storage interfaces and should
13 // be used as the main point of interaction for the data storage
16 template *template.Template
23 // Render uses the HTML templates associated with the Context to render the
24 // template specified by name to out using data to fill any template variables.
25 func (c Context) Render(out io.Writer, name string, data interface{}) {
26 if c.template == nil {
27 log.Println("No template set on Context, can't render anything!")
30 err := c.template.ExecuteTemplate(out, name, data)
32 log.Println("Error executing template", name, ":", err)
36 // GetClient returns a single Client by its ID from the
37 // clientStore associated with the Context.
38 func (c Context) GetClient(id uuid.ID) (Client, error) {
40 return Client{}, ErrNoClientStore
42 return c.clients.getClient(id)
45 // SaveClient stores the passed Client in the clientStore
46 // associated with the Context.
47 func (c Context) SaveClient(client Client) error {
49 return ErrNoClientStore
51 return c.clients.saveClient(client)
54 // UpdateClient applies the specified ClientChange to the Client
55 // with the specified ID in the clientStore associated with the
57 func (c Context) UpdateClient(id uuid.ID, change ClientChange) error {
59 return ErrNoClientStore
61 return c.clients.updateClient(id, change)
64 // DeleteClient removes the client with the specified ID from the
65 // clientStore associated with the Context.
66 func (c Context) DeleteClient(id uuid.ID) error {
68 return ErrNoClientStore
70 return c.clients.deleteClient(id)
73 // ListClientsByOwner returns a slice of up to num Clients, starting at offset (inclusive)
74 // that have the specified OwnerID in the clientStore associated with the Context.
75 func (c Context) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) {
77 return []Client{}, ErrNoClientStore
79 return c.clients.listClientsByOwner(ownerID, num, offset)
82 // AddEndpoint stores the specified Endpoint in the clientStore associated with the Context,
83 // and associates the newly-stored Endpoint with the Client specified by the passed ID.
84 func (c Context) AddEndpoint(client uuid.ID, endpoint Endpoint) error {
86 return ErrNoClientStore
88 return c.clients.addEndpoint(client, endpoint)
91 // RemoveEndpoint deletes the Endpoint with the specified ID from the clientStore associated
92 // with the Context, and disassociates the Endpoint from the specified Client.
93 func (c Context) RemoveEndpoint(client, endpoint uuid.ID) error {
95 return ErrNoClientStore
97 return c.clients.removeEndpoint(client, endpoint)
100 // CheckEndpoint finds Endpoints in the clientStore associated with the Context that belong
101 // to the Client specified by the passed ID and match the URI passed. URI matches must be
102 // performed according to RFC 3986 Section 6.
103 func (c Context) CheckEndpoint(client uuid.ID, URI string) (bool, error) {
104 if c.clients == nil {
105 return false, ErrNoClientStore
107 return c.clients.checkEndpoint(client, URI)
110 // ListEndpoints finds Endpoints in the clientStore associated with the Context that belong
111 // to the Client specified by the passed ID. It returns up to num endpoints, starting at offset,
113 func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) {
114 if c.clients == nil {
115 return []Endpoint{}, ErrNoClientStore
117 return c.clients.listEndpoints(client, num, offset)
120 // CountEndpoints returns the number of Endpoints the are associated with the Client specified by the
121 // passed ID in the clientStore associated with the Context.
122 func (c Context) CountEndpoints(client uuid.ID) (int64, error) {
123 if c.clients == nil {
124 return 0, ErrNoClientStore
126 return c.clients.countEndpoints(client)
129 // GetGrant returns the Grant specified by the provided code from the grantStore associated with the
131 func (c Context) GetGrant(code string) (Grant, error) {
133 return Grant{}, ErrNoGrantStore
135 return c.grants.getGrant(code)
138 // SaveGrant stores the passed Grant in the grantStore associated with the Context.
139 func (c Context) SaveGrant(grant Grant) error {
141 return ErrNoGrantStore
143 return c.grants.saveGrant(grant)
146 // DeleteGrant removes the Grant specified by the provided code from the grantStore associated with
148 func (c Context) DeleteGrant(code string) error {
150 return ErrNoGrantStore
152 return c.grants.deleteGrant(code)
155 // GetProfileByID returns the Profile specified by the provided ID from the profileStore associated with
157 func (c Context) GetProfileByID(id uuid.ID) (Profile, error) {
158 if c.profiles == nil {
159 return Profile{}, ErrNoProfileStore
161 return c.profiles.getProfileByID(id)
164 // GetProfileByLogin returns the Profile associated with the specified Login from the profileStore associated
166 func (c Context) GetProfileByLogin(loginType, value string) (Profile, error) {
167 if c.profiles == nil {
168 return Profile{}, ErrNoProfileStore
170 return c.profiles.getProfileByLogin(loginType, value)
173 // SaveProfile inserts the passed Profile into the profileStore associated with the Context.
174 func (c Context) SaveProfile(profile Profile) error {
175 if c.profiles == nil {
176 return ErrNoProfileStore
178 return c.profiles.saveProfile(profile)
181 // UpdateProfile applies the supplied ProfileChange to the Profile that matches the specified ID
182 // in the profileStore associated with the Context.
183 func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error {
184 if c.profiles == nil {
185 return ErrNoProfileStore
187 return c.profiles.updateProfile(id, change)
190 // UpdateProfiles applies the supplied BulkProfileChange to every Profile that matches one of the
191 // specified IDs in the profileStore associated with the Context.
192 func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error {
193 if c.profiles == nil {
194 return ErrNoProfileStore
196 return c.profiles.updateProfiles(ids, change)
199 // DeleteProfile removes the Profile specified by the passed ID from the profileStore associated
201 func (c Context) DeleteProfile(id uuid.ID) error {
202 if c.profiles == nil {
203 return ErrNoProfileStore
205 return c.profiles.deleteProfile(id)
208 // AddLogin stores the passed Login in the profileStore associated with the Context. It also associates
209 // the newly-created Login with the Orofile in login.ProfileID.
210 func (c Context) AddLogin(login Login) error {
211 if c.profiles == nil {
212 return ErrNoProfileStore
214 return c.profiles.addLogin(login)
217 // RemoveLogin removes the specified Login from the profileStore associated with the Context, provided
218 // the Login has a ProfileID property that matches the profile ID passed in. It also disassociates the
219 // deleted Login from the Profile in login.ProfileID.
220 func (c Context) RemoveLogin(loginType, value string, profile uuid.ID) error {
221 if c.profiles == nil {
222 return ErrNoProfileStore
224 return c.profiles.removeLogin(loginType, value, profile)
227 // RecordLoginUse sets the LastUsed property of the Login specified in the profileStore associated with
228 // the Context to the value passed in as when.
229 func (c Context) RecordLoginUse(loginType, value string, when time.Time) error {
230 if c.profiles == nil {
231 return ErrNoProfileStore
233 return c.profiles.recordLoginUse(loginType, value, when)
236 // ListLogins returns a slice of up to num Logins associated with the specified Profile from the profileStore
237 // associated with the Context, skipping offset Profiles.
238 func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) {
239 if c.profiles == nil {
240 return []Login{}, ErrNoProfileStore
242 return c.profiles.listLogins(profile, num, offset)
245 // GetToken returns the Token specified from the tokenStore associated with the Context.
246 // If refresh is true, the token input should be compared against the refresh tokens, not the
248 func (c Context) GetToken(token string, refresh bool) (Token, error) {
250 return Token{}, ErrNoTokenStore
252 return c.tokens.getToken(token, refresh)
255 // SaveToken stores the passed Token in the tokenStore associated with the Context.
256 func (c Context) SaveToken(token Token) error {
258 return ErrNoTokenStore
260 return c.tokens.saveToken(token)
263 // RemoveToken removes the Token identified by the passed token string from the tokenStore associated
265 func (c Context) RemoveToken(token string) error {
267 return ErrNoTokenStore
269 return c.tokens.removeToken(token)
272 // GetTokensByProfileID returns a slice of up to num Tokens with a ProfileID that matches the specified
273 // profileID from the tokenStore associated with the Context, skipping offset Tokens.
274 func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) {
276 return []Token{}, ErrNoTokenStore
278 return c.tokens.getTokensByProfileID(profileID, num, offset)