auth
auth/context.go
Add TODOs. There's still a lot of the OAuth2 spec to implement, but we're making good progress. Let's keep track of the remaining pieces using TODO markers.
| paddy@50 | 1 package auth |
| paddy@50 | 2 |
| paddy@50 | 3 import ( |
| paddy@50 | 4 "html/template" |
| paddy@50 | 5 "io" |
| paddy@55 | 6 "log" |
| paddy@50 | 7 "time" |
| paddy@50 | 8 |
| paddy@50 | 9 "code.secondbit.org/uuid" |
| paddy@50 | 10 ) |
| paddy@50 | 11 |
| paddy@57 | 12 // Context wraps the different storage interfaces and should |
| paddy@57 | 13 // be used as the main point of interaction for the data storage |
| paddy@57 | 14 // layer. |
| paddy@50 | 15 type Context struct { |
| paddy@50 | 16 template *template.Template |
| paddy@57 | 17 clients clientStore |
| paddy@57 | 18 grants grantStore |
| paddy@57 | 19 profiles profileStore |
| paddy@57 | 20 tokens tokenStore |
| paddy@50 | 21 } |
| paddy@50 | 22 |
| paddy@57 | 23 // Render uses the HTML templates associated with the Context to render the |
| paddy@57 | 24 // template specified by name to out using data to fill any template variables. |
| paddy@55 | 25 func (c Context) Render(out io.Writer, name string, data interface{}) { |
| paddy@50 | 26 if c.template == nil { |
| paddy@57 | 27 log.Println("No template set on Context, can't render anything!") |
| paddy@57 | 28 return |
| paddy@50 | 29 } |
| paddy@55 | 30 err := c.template.ExecuteTemplate(out, name, data) |
| paddy@55 | 31 if err != nil { |
| paddy@55 | 32 log.Println("Error executing template", name, ":", err) |
| paddy@55 | 33 } |
| paddy@50 | 34 } |
| paddy@50 | 35 |
| paddy@57 | 36 // GetClient returns a single Client by its ID from the |
| paddy@57 | 37 // clientStore associated with the Context. |
| paddy@50 | 38 func (c Context) GetClient(id uuid.ID) (Client, error) { |
| paddy@50 | 39 if c.clients == nil { |
| paddy@50 | 40 return Client{}, ErrNoClientStore |
| paddy@50 | 41 } |
| paddy@57 | 42 return c.clients.getClient(id) |
| paddy@50 | 43 } |
| paddy@50 | 44 |
| paddy@57 | 45 // SaveClient stores the passed Client in the clientStore |
| paddy@57 | 46 // associated with the Context. |
| paddy@50 | 47 func (c Context) SaveClient(client Client) error { |
| paddy@50 | 48 if c.clients == nil { |
| paddy@50 | 49 return ErrNoClientStore |
| paddy@50 | 50 } |
| paddy@57 | 51 return c.clients.saveClient(client) |
| paddy@50 | 52 } |
| paddy@50 | 53 |
| paddy@57 | 54 // UpdateClient applies the specified ClientChange to the Client |
| paddy@57 | 55 // with the specified ID in the clientStore associated with the |
| paddy@57 | 56 // Context. |
| paddy@50 | 57 func (c Context) UpdateClient(id uuid.ID, change ClientChange) error { |
| paddy@50 | 58 if c.clients == nil { |
| paddy@50 | 59 return ErrNoClientStore |
| paddy@50 | 60 } |
| paddy@57 | 61 return c.clients.updateClient(id, change) |
| paddy@50 | 62 } |
| paddy@50 | 63 |
| paddy@57 | 64 // DeleteClient removes the client with the specified ID from the |
| paddy@57 | 65 // clientStore associated with the Context. |
| paddy@50 | 66 func (c Context) DeleteClient(id uuid.ID) error { |
| paddy@50 | 67 if c.clients == nil { |
| paddy@50 | 68 return ErrNoClientStore |
| paddy@50 | 69 } |
| paddy@57 | 70 return c.clients.deleteClient(id) |
| paddy@50 | 71 } |
| paddy@50 | 72 |
| paddy@57 | 73 // ListClientsByOwner returns a slice of up to num Clients, starting at offset (inclusive) |
| paddy@57 | 74 // that have the specified OwnerID in the clientStore associated with the Context. |
| paddy@50 | 75 func (c Context) ListClientsByOwner(ownerID uuid.ID, num, offset int) ([]Client, error) { |
| paddy@50 | 76 if c.clients == nil { |
| paddy@50 | 77 return []Client{}, ErrNoClientStore |
| paddy@50 | 78 } |
| paddy@57 | 79 return c.clients.listClientsByOwner(ownerID, num, offset) |
| paddy@50 | 80 } |
| paddy@50 | 81 |
| paddy@57 | 82 // AddEndpoint stores the specified Endpoint in the clientStore associated with the Context, |
| paddy@57 | 83 // and associates the newly-stored Endpoint with the Client specified by the passed ID. |
| paddy@50 | 84 func (c Context) AddEndpoint(client uuid.ID, endpoint Endpoint) error { |
| paddy@50 | 85 if c.clients == nil { |
| paddy@50 | 86 return ErrNoClientStore |
| paddy@50 | 87 } |
| paddy@57 | 88 return c.clients.addEndpoint(client, endpoint) |
| paddy@50 | 89 } |
| paddy@50 | 90 |
| paddy@57 | 91 // RemoveEndpoint deletes the Endpoint with the specified ID from the clientStore associated |
| paddy@57 | 92 // with the Context, and disassociates the Endpoint from the specified Client. |
| paddy@50 | 93 func (c Context) RemoveEndpoint(client, endpoint uuid.ID) error { |
| paddy@50 | 94 if c.clients == nil { |
| paddy@50 | 95 return ErrNoClientStore |
| paddy@50 | 96 } |
| paddy@57 | 97 return c.clients.removeEndpoint(client, endpoint) |
| paddy@50 | 98 } |
| paddy@50 | 99 |
| paddy@57 | 100 // CheckEndpoint finds Endpoints in the clientStore associated with the Context that belong |
| paddy@58 | 101 // to the Client specified by the passed ID and match the URI passed. URI matches must be |
| paddy@58 | 102 // performed according to RFC 3986 Section 6. |
| paddy@58 | 103 func (c Context) CheckEndpoint(client uuid.ID, URI string) (bool, error) { |
| paddy@50 | 104 if c.clients == nil { |
| paddy@50 | 105 return false, ErrNoClientStore |
| paddy@50 | 106 } |
| paddy@58 | 107 return c.clients.checkEndpoint(client, URI) |
| paddy@50 | 108 } |
| paddy@50 | 109 |
| paddy@57 | 110 // ListEndpoints finds Endpoints in the clientStore associated with the Context that belong |
| paddy@57 | 111 // to the Client specified by the passed ID. It returns up to num endpoints, starting at offset, |
| paddy@57 | 112 // exclusive. |
| paddy@50 | 113 func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) { |
| paddy@50 | 114 if c.clients == nil { |
| paddy@50 | 115 return []Endpoint{}, ErrNoClientStore |
| paddy@50 | 116 } |
| paddy@57 | 117 return c.clients.listEndpoints(client, num, offset) |
| paddy@50 | 118 } |
| paddy@50 | 119 |
| paddy@57 | 120 // CountEndpoints returns the number of Endpoints the are associated with the Client specified by the |
| paddy@57 | 121 // passed ID in the clientStore associated with the Context. |
| paddy@55 | 122 func (c Context) CountEndpoints(client uuid.ID) (int64, error) { |
| paddy@55 | 123 if c.clients == nil { |
| paddy@55 | 124 return 0, ErrNoClientStore |
| paddy@55 | 125 } |
| paddy@57 | 126 return c.clients.countEndpoints(client) |
| paddy@55 | 127 } |
| paddy@55 | 128 |
| paddy@57 | 129 // GetGrant returns the Grant specified by the provided code from the grantStore associated with the |
| paddy@57 | 130 // Context. |
| paddy@50 | 131 func (c Context) GetGrant(code string) (Grant, error) { |
| paddy@50 | 132 if c.grants == nil { |
| paddy@50 | 133 return Grant{}, ErrNoGrantStore |
| paddy@50 | 134 } |
| paddy@57 | 135 return c.grants.getGrant(code) |
| paddy@50 | 136 } |
| paddy@50 | 137 |
| paddy@57 | 138 // SaveGrant stores the passed Grant in the grantStore associated with the Context. |
| paddy@50 | 139 func (c Context) SaveGrant(grant Grant) error { |
| paddy@50 | 140 if c.grants == nil { |
| paddy@50 | 141 return ErrNoGrantStore |
| paddy@50 | 142 } |
| paddy@57 | 143 return c.grants.saveGrant(grant) |
| paddy@50 | 144 } |
| paddy@50 | 145 |
| paddy@57 | 146 // DeleteGrant removes the Grant specified by the provided code from the grantStore associated with |
| paddy@57 | 147 // the Context. |
| paddy@50 | 148 func (c Context) DeleteGrant(code string) error { |
| paddy@50 | 149 if c.grants == nil { |
| paddy@50 | 150 return ErrNoGrantStore |
| paddy@50 | 151 } |
| paddy@57 | 152 return c.grants.deleteGrant(code) |
| paddy@50 | 153 } |
| paddy@50 | 154 |
| paddy@57 | 155 // GetProfileByID returns the Profile specified by the provided ID from the profileStore associated with |
| paddy@57 | 156 // the Context. |
| paddy@50 | 157 func (c Context) GetProfileByID(id uuid.ID) (Profile, error) { |
| paddy@50 | 158 if c.profiles == nil { |
| paddy@50 | 159 return Profile{}, ErrNoProfileStore |
| paddy@50 | 160 } |
| paddy@57 | 161 return c.profiles.getProfileByID(id) |
| paddy@50 | 162 } |
| paddy@50 | 163 |
| paddy@57 | 164 // GetProfileByLogin returns the Profile associated with the specified Login from the profileStore associated |
| paddy@57 | 165 // with the Context. |
| paddy@50 | 166 func (c Context) GetProfileByLogin(loginType, value string) (Profile, error) { |
| paddy@50 | 167 if c.profiles == nil { |
| paddy@50 | 168 return Profile{}, ErrNoProfileStore |
| paddy@50 | 169 } |
| paddy@57 | 170 return c.profiles.getProfileByLogin(loginType, value) |
| paddy@50 | 171 } |
| paddy@50 | 172 |
| paddy@57 | 173 // SaveProfile inserts the passed Profile into the profileStore associated with the Context. |
| paddy@50 | 174 func (c Context) SaveProfile(profile Profile) error { |
| paddy@50 | 175 if c.profiles == nil { |
| paddy@50 | 176 return ErrNoProfileStore |
| paddy@50 | 177 } |
| paddy@57 | 178 return c.profiles.saveProfile(profile) |
| paddy@50 | 179 } |
| paddy@50 | 180 |
| paddy@57 | 181 // UpdateProfile applies the supplied ProfileChange to the Profile that matches the specified ID |
| paddy@57 | 182 // in the profileStore associated with the Context. |
| paddy@50 | 183 func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error { |
| paddy@50 | 184 if c.profiles == nil { |
| paddy@50 | 185 return ErrNoProfileStore |
| paddy@50 | 186 } |
| paddy@57 | 187 return c.profiles.updateProfile(id, change) |
| paddy@50 | 188 } |
| paddy@50 | 189 |
| paddy@57 | 190 // UpdateProfiles applies the supplied BulkProfileChange to every Profile that matches one of the |
| paddy@57 | 191 // specified IDs in the profileStore associated with the Context. |
| paddy@50 | 192 func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error { |
| paddy@50 | 193 if c.profiles == nil { |
| paddy@50 | 194 return ErrNoProfileStore |
| paddy@50 | 195 } |
| paddy@57 | 196 return c.profiles.updateProfiles(ids, change) |
| paddy@50 | 197 } |
| paddy@50 | 198 |
| paddy@57 | 199 // DeleteProfile removes the Profile specified by the passed ID from the profileStore associated |
| paddy@57 | 200 // with the Context. |
| paddy@50 | 201 func (c Context) DeleteProfile(id uuid.ID) error { |
| paddy@50 | 202 if c.profiles == nil { |
| paddy@50 | 203 return ErrNoProfileStore |
| paddy@50 | 204 } |
| paddy@57 | 205 return c.profiles.deleteProfile(id) |
| paddy@50 | 206 } |
| paddy@50 | 207 |
| paddy@57 | 208 // AddLogin stores the passed Login in the profileStore associated with the Context. It also associates |
| paddy@57 | 209 // the newly-created Login with the Orofile in login.ProfileID. |
| paddy@50 | 210 func (c Context) AddLogin(login Login) error { |
| paddy@50 | 211 if c.profiles == nil { |
| paddy@50 | 212 return ErrNoProfileStore |
| paddy@50 | 213 } |
| paddy@57 | 214 return c.profiles.addLogin(login) |
| paddy@50 | 215 } |
| paddy@50 | 216 |
| paddy@57 | 217 // RemoveLogin removes the specified Login from the profileStore associated with the Context, provided |
| paddy@57 | 218 // the Login has a ProfileID property that matches the profile ID passed in. It also disassociates the |
| paddy@57 | 219 // deleted Login from the Profile in login.ProfileID. |
| paddy@50 | 220 func (c Context) RemoveLogin(loginType, value string, profile uuid.ID) error { |
| paddy@50 | 221 if c.profiles == nil { |
| paddy@50 | 222 return ErrNoProfileStore |
| paddy@50 | 223 } |
| paddy@57 | 224 return c.profiles.removeLogin(loginType, value, profile) |
| paddy@50 | 225 } |
| paddy@50 | 226 |
| paddy@57 | 227 // RecordLoginUse sets the LastUsed property of the Login specified in the profileStore associated with |
| paddy@57 | 228 // the Context to the value passed in as when. |
| paddy@50 | 229 func (c Context) RecordLoginUse(loginType, value string, when time.Time) error { |
| paddy@50 | 230 if c.profiles == nil { |
| paddy@50 | 231 return ErrNoProfileStore |
| paddy@50 | 232 } |
| paddy@57 | 233 return c.profiles.recordLoginUse(loginType, value, when) |
| paddy@50 | 234 } |
| paddy@50 | 235 |
| paddy@57 | 236 // ListLogins returns a slice of up to num Logins associated with the specified Profile from the profileStore |
| paddy@57 | 237 // associated with the Context, skipping offset Profiles. |
| paddy@50 | 238 func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) { |
| paddy@50 | 239 if c.profiles == nil { |
| paddy@50 | 240 return []Login{}, ErrNoProfileStore |
| paddy@50 | 241 } |
| paddy@57 | 242 return c.profiles.listLogins(profile, num, offset) |
| paddy@50 | 243 } |
| paddy@50 | 244 |
| paddy@57 | 245 // GetToken returns the Token specified from the tokenStore associated with the Context. |
| paddy@57 | 246 // If refresh is true, the token input should be compared against the refresh tokens, not the |
| paddy@57 | 247 // access tokens. |
| paddy@50 | 248 func (c Context) GetToken(token string, refresh bool) (Token, error) { |
| paddy@50 | 249 if c.tokens == nil { |
| paddy@50 | 250 return Token{}, ErrNoTokenStore |
| paddy@50 | 251 } |
| paddy@57 | 252 return c.tokens.getToken(token, refresh) |
| paddy@50 | 253 } |
| paddy@50 | 254 |
| paddy@57 | 255 // SaveToken stores the passed Token in the tokenStore associated with the Context. |
| paddy@50 | 256 func (c Context) SaveToken(token Token) error { |
| paddy@50 | 257 if c.tokens == nil { |
| paddy@50 | 258 return ErrNoTokenStore |
| paddy@50 | 259 } |
| paddy@57 | 260 return c.tokens.saveToken(token) |
| paddy@50 | 261 } |
| paddy@50 | 262 |
| paddy@57 | 263 // RemoveToken removes the Token identified by the passed token string from the tokenStore associated |
| paddy@57 | 264 // with the Context. |
| paddy@50 | 265 func (c Context) RemoveToken(token string) error { |
| paddy@50 | 266 if c.tokens == nil { |
| paddy@50 | 267 return ErrNoTokenStore |
| paddy@50 | 268 } |
| paddy@57 | 269 return c.tokens.removeToken(token) |
| paddy@50 | 270 } |
| paddy@50 | 271 |
| paddy@57 | 272 // GetTokensByProfileID returns a slice of up to num Tokens with a ProfileID that matches the specified |
| paddy@57 | 273 // profileID from the tokenStore associated with the Context, skipping offset Tokens. |
| paddy@50 | 274 func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) { |
| paddy@50 | 275 if c.tokens == nil { |
| paddy@50 | 276 return []Token{}, ErrNoTokenStore |
| paddy@50 | 277 } |
| paddy@57 | 278 return c.tokens.getTokensByProfileID(profileID, num, offset) |
| paddy@50 | 279 } |