auth
auth/context.go
The great documentation and exported interface cleanup. Modify all our *Store interfaces to be unexported, as there's no real good reason they need to be exported, especially as they can be implemented without being exported. The interfaces shouldn't matter to 99% of users of the package, so let's not pollute our package API. Further, all methods of the interfaces are now unexported, for pretty much the same reasoning. Add a doc.go file with documentation explaining the choices the package is making and what it provides. Implement documentation on all our exported types and methods and functions, which makes golint happy. The only remaining golint warning is about NewMemstore, which will stay the way it is. The memstore type is useful outside tests for things like standing up a server quickly when we don't care about the storage, and because the type is unexported, we _need_ a New function to create an instance that can be passed to the Context.
| 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@57 | 101 // to the Client specified by the passed ID and match the URI passed. If strict is true, only |
| paddy@57 | 102 // exact matches for the URI will be returned. If it is false, matches are performed according |
| paddy@57 | 103 // to RFC 3986 Section 6. |
| paddy@55 | 104 func (c Context) CheckEndpoint(client uuid.ID, URI string, strict bool) (bool, error) { |
| paddy@50 | 105 if c.clients == nil { |
| paddy@50 | 106 return false, ErrNoClientStore |
| paddy@50 | 107 } |
| paddy@57 | 108 return c.clients.checkEndpoint(client, URI, strict) |
| paddy@50 | 109 } |
| paddy@50 | 110 |
| paddy@57 | 111 // ListEndpoints finds Endpoints in the clientStore associated with the Context that belong |
| paddy@57 | 112 // to the Client specified by the passed ID. It returns up to num endpoints, starting at offset, |
| paddy@57 | 113 // exclusive. |
| paddy@50 | 114 func (c Context) ListEndpoints(client uuid.ID, num, offset int) ([]Endpoint, error) { |
| paddy@50 | 115 if c.clients == nil { |
| paddy@50 | 116 return []Endpoint{}, ErrNoClientStore |
| paddy@50 | 117 } |
| paddy@57 | 118 return c.clients.listEndpoints(client, num, offset) |
| paddy@50 | 119 } |
| paddy@50 | 120 |
| paddy@57 | 121 // CountEndpoints returns the number of Endpoints the are associated with the Client specified by the |
| paddy@57 | 122 // passed ID in the clientStore associated with the Context. |
| paddy@55 | 123 func (c Context) CountEndpoints(client uuid.ID) (int64, error) { |
| paddy@55 | 124 if c.clients == nil { |
| paddy@55 | 125 return 0, ErrNoClientStore |
| paddy@55 | 126 } |
| paddy@57 | 127 return c.clients.countEndpoints(client) |
| paddy@55 | 128 } |
| paddy@55 | 129 |
| paddy@57 | 130 // GetGrant returns the Grant specified by the provided code from the grantStore associated with the |
| paddy@57 | 131 // Context. |
| paddy@50 | 132 func (c Context) GetGrant(code string) (Grant, error) { |
| paddy@50 | 133 if c.grants == nil { |
| paddy@50 | 134 return Grant{}, ErrNoGrantStore |
| paddy@50 | 135 } |
| paddy@57 | 136 return c.grants.getGrant(code) |
| paddy@50 | 137 } |
| paddy@50 | 138 |
| paddy@57 | 139 // SaveGrant stores the passed Grant in the grantStore associated with the Context. |
| paddy@50 | 140 func (c Context) SaveGrant(grant Grant) error { |
| paddy@50 | 141 if c.grants == nil { |
| paddy@50 | 142 return ErrNoGrantStore |
| paddy@50 | 143 } |
| paddy@57 | 144 return c.grants.saveGrant(grant) |
| paddy@50 | 145 } |
| paddy@50 | 146 |
| paddy@57 | 147 // DeleteGrant removes the Grant specified by the provided code from the grantStore associated with |
| paddy@57 | 148 // the Context. |
| paddy@50 | 149 func (c Context) DeleteGrant(code string) error { |
| paddy@50 | 150 if c.grants == nil { |
| paddy@50 | 151 return ErrNoGrantStore |
| paddy@50 | 152 } |
| paddy@57 | 153 return c.grants.deleteGrant(code) |
| paddy@50 | 154 } |
| paddy@50 | 155 |
| paddy@57 | 156 // GetProfileByID returns the Profile specified by the provided ID from the profileStore associated with |
| paddy@57 | 157 // the Context. |
| paddy@50 | 158 func (c Context) GetProfileByID(id uuid.ID) (Profile, error) { |
| paddy@50 | 159 if c.profiles == nil { |
| paddy@50 | 160 return Profile{}, ErrNoProfileStore |
| paddy@50 | 161 } |
| paddy@57 | 162 return c.profiles.getProfileByID(id) |
| paddy@50 | 163 } |
| paddy@50 | 164 |
| paddy@57 | 165 // GetProfileByLogin returns the Profile associated with the specified Login from the profileStore associated |
| paddy@57 | 166 // with the Context. |
| paddy@50 | 167 func (c Context) GetProfileByLogin(loginType, value string) (Profile, error) { |
| paddy@50 | 168 if c.profiles == nil { |
| paddy@50 | 169 return Profile{}, ErrNoProfileStore |
| paddy@50 | 170 } |
| paddy@57 | 171 return c.profiles.getProfileByLogin(loginType, value) |
| paddy@50 | 172 } |
| paddy@50 | 173 |
| paddy@57 | 174 // SaveProfile inserts the passed Profile into the profileStore associated with the Context. |
| paddy@50 | 175 func (c Context) SaveProfile(profile Profile) error { |
| paddy@50 | 176 if c.profiles == nil { |
| paddy@50 | 177 return ErrNoProfileStore |
| paddy@50 | 178 } |
| paddy@57 | 179 return c.profiles.saveProfile(profile) |
| paddy@50 | 180 } |
| paddy@50 | 181 |
| paddy@57 | 182 // UpdateProfile applies the supplied ProfileChange to the Profile that matches the specified ID |
| paddy@57 | 183 // in the profileStore associated with the Context. |
| paddy@50 | 184 func (c Context) UpdateProfile(id uuid.ID, change ProfileChange) error { |
| paddy@50 | 185 if c.profiles == nil { |
| paddy@50 | 186 return ErrNoProfileStore |
| paddy@50 | 187 } |
| paddy@57 | 188 return c.profiles.updateProfile(id, change) |
| paddy@50 | 189 } |
| paddy@50 | 190 |
| paddy@57 | 191 // UpdateProfiles applies the supplied BulkProfileChange to every Profile that matches one of the |
| paddy@57 | 192 // specified IDs in the profileStore associated with the Context. |
| paddy@50 | 193 func (c Context) UpdateProfiles(ids []uuid.ID, change BulkProfileChange) error { |
| paddy@50 | 194 if c.profiles == nil { |
| paddy@50 | 195 return ErrNoProfileStore |
| paddy@50 | 196 } |
| paddy@57 | 197 return c.profiles.updateProfiles(ids, change) |
| paddy@50 | 198 } |
| paddy@50 | 199 |
| paddy@57 | 200 // DeleteProfile removes the Profile specified by the passed ID from the profileStore associated |
| paddy@57 | 201 // with the Context. |
| paddy@50 | 202 func (c Context) DeleteProfile(id uuid.ID) error { |
| paddy@50 | 203 if c.profiles == nil { |
| paddy@50 | 204 return ErrNoProfileStore |
| paddy@50 | 205 } |
| paddy@57 | 206 return c.profiles.deleteProfile(id) |
| paddy@50 | 207 } |
| paddy@50 | 208 |
| paddy@57 | 209 // AddLogin stores the passed Login in the profileStore associated with the Context. It also associates |
| paddy@57 | 210 // the newly-created Login with the Orofile in login.ProfileID. |
| paddy@50 | 211 func (c Context) AddLogin(login Login) error { |
| paddy@50 | 212 if c.profiles == nil { |
| paddy@50 | 213 return ErrNoProfileStore |
| paddy@50 | 214 } |
| paddy@57 | 215 return c.profiles.addLogin(login) |
| paddy@50 | 216 } |
| paddy@50 | 217 |
| paddy@57 | 218 // RemoveLogin removes the specified Login from the profileStore associated with the Context, provided |
| paddy@57 | 219 // the Login has a ProfileID property that matches the profile ID passed in. It also disassociates the |
| paddy@57 | 220 // deleted Login from the Profile in login.ProfileID. |
| paddy@50 | 221 func (c Context) RemoveLogin(loginType, value string, profile uuid.ID) error { |
| paddy@50 | 222 if c.profiles == nil { |
| paddy@50 | 223 return ErrNoProfileStore |
| paddy@50 | 224 } |
| paddy@57 | 225 return c.profiles.removeLogin(loginType, value, profile) |
| paddy@50 | 226 } |
| paddy@50 | 227 |
| paddy@57 | 228 // RecordLoginUse sets the LastUsed property of the Login specified in the profileStore associated with |
| paddy@57 | 229 // the Context to the value passed in as when. |
| paddy@50 | 230 func (c Context) RecordLoginUse(loginType, value string, when time.Time) error { |
| paddy@50 | 231 if c.profiles == nil { |
| paddy@50 | 232 return ErrNoProfileStore |
| paddy@50 | 233 } |
| paddy@57 | 234 return c.profiles.recordLoginUse(loginType, value, when) |
| paddy@50 | 235 } |
| paddy@50 | 236 |
| paddy@57 | 237 // ListLogins returns a slice of up to num Logins associated with the specified Profile from the profileStore |
| paddy@57 | 238 // associated with the Context, skipping offset Profiles. |
| paddy@50 | 239 func (c Context) ListLogins(profile uuid.ID, num, offset int) ([]Login, error) { |
| paddy@50 | 240 if c.profiles == nil { |
| paddy@50 | 241 return []Login{}, ErrNoProfileStore |
| paddy@50 | 242 } |
| paddy@57 | 243 return c.profiles.listLogins(profile, num, offset) |
| paddy@50 | 244 } |
| paddy@50 | 245 |
| paddy@57 | 246 // GetToken returns the Token specified from the tokenStore associated with the Context. |
| paddy@57 | 247 // If refresh is true, the token input should be compared against the refresh tokens, not the |
| paddy@57 | 248 // access tokens. |
| paddy@50 | 249 func (c Context) GetToken(token string, refresh bool) (Token, error) { |
| paddy@50 | 250 if c.tokens == nil { |
| paddy@50 | 251 return Token{}, ErrNoTokenStore |
| paddy@50 | 252 } |
| paddy@57 | 253 return c.tokens.getToken(token, refresh) |
| paddy@50 | 254 } |
| paddy@50 | 255 |
| paddy@57 | 256 // SaveToken stores the passed Token in the tokenStore associated with the Context. |
| paddy@50 | 257 func (c Context) SaveToken(token Token) error { |
| paddy@50 | 258 if c.tokens == nil { |
| paddy@50 | 259 return ErrNoTokenStore |
| paddy@50 | 260 } |
| paddy@57 | 261 return c.tokens.saveToken(token) |
| paddy@50 | 262 } |
| paddy@50 | 263 |
| paddy@57 | 264 // RemoveToken removes the Token identified by the passed token string from the tokenStore associated |
| paddy@57 | 265 // with the Context. |
| paddy@50 | 266 func (c Context) RemoveToken(token string) error { |
| paddy@50 | 267 if c.tokens == nil { |
| paddy@50 | 268 return ErrNoTokenStore |
| paddy@50 | 269 } |
| paddy@57 | 270 return c.tokens.removeToken(token) |
| paddy@50 | 271 } |
| paddy@50 | 272 |
| paddy@57 | 273 // GetTokensByProfileID returns a slice of up to num Tokens with a ProfileID that matches the specified |
| paddy@57 | 274 // profileID from the tokenStore associated with the Context, skipping offset Tokens. |
| paddy@50 | 275 func (c Context) GetTokensByProfileID(profileID uuid.ID, num, offset int) ([]Token, error) { |
| paddy@50 | 276 if c.tokens == nil { |
| paddy@50 | 277 return []Token{}, ErrNoTokenStore |
| paddy@50 | 278 } |
| paddy@57 | 279 return c.tokens.getTokensByProfileID(profileID, num, offset) |
| paddy@50 | 280 } |