auth
2015-12-14
Parent:b7e685839a1b
auth/authcode.go
Update nsq import path. go-nsq has moved to nsqio/go-nsq, so we need to update the import path appropriately.
| paddy@26 | 1 package auth |
| paddy@26 | 2 |
| paddy@26 | 3 import ( |
| paddy@84 | 4 "encoding/json" |
| paddy@29 | 5 "errors" |
| paddy@84 | 6 "net/http" |
| paddy@26 | 7 "time" |
| paddy@26 | 8 |
| paddy@181 | 9 "code.secondbit.org/scopes.hg/types" |
| paddy@107 | 10 "code.secondbit.org/uuid.hg" |
| paddy@26 | 11 ) |
| paddy@26 | 12 |
| paddy@84 | 13 func init() { |
| paddy@84 | 14 RegisterGrantType("authorization_code", GrantType{ |
| paddy@84 | 15 Validate: authCodeGrantValidate, |
| paddy@94 | 16 Invalidate: authCodeGrantInvalidate, |
| paddy@84 | 17 IssuesRefresh: true, |
| paddy@85 | 18 ReturnToken: RenderJSONToken, |
| paddy@123 | 19 AllowsPublic: true, |
| paddy@124 | 20 AuditString: authCodeGrantAuditString, |
| paddy@84 | 21 }) |
| paddy@84 | 22 } |
| paddy@84 | 23 |
| paddy@29 | 24 var ( |
| paddy@87 | 25 // ErrNoAuthorizationCodeStore is returned when a Context tries to act on a authorizationCodeStore without setting one first. |
| paddy@87 | 26 ErrNoAuthorizationCodeStore = errors.New("no authorizationCodeStore was specified for the Context") |
| paddy@87 | 27 // ErrAuthorizationCodeNotFound is returned when an AuthorizationCode is requested but not found in the authorizationCodeStore. |
| paddy@87 | 28 ErrAuthorizationCodeNotFound = errors.New("authorization code not found in authorizationCodeStore") |
| paddy@87 | 29 // ErrAuthorizationCodeAlreadyExists is returned when an AuthorizationCode is added to a authorizationCodeStore, but another AuthorizationCode with the |
| paddy@87 | 30 // same Code already exists in the authorizationCodeStore. |
| paddy@87 | 31 ErrAuthorizationCodeAlreadyExists = errors.New("authorization code already exists in authorizationCodeStore") |
| paddy@29 | 32 ) |
| paddy@29 | 33 |
| paddy@87 | 34 // AuthorizationCode represents an authorization grant made by a user to a Client, to |
| paddy@57 | 35 // access user data within a defined Scope for a limited amount of time. |
| paddy@87 | 36 type AuthorizationCode struct { |
| paddy@26 | 37 Code string |
| paddy@26 | 38 Created time.Time |
| paddy@26 | 39 ExpiresIn int32 |
| paddy@26 | 40 ClientID uuid.ID |
| paddy@181 | 41 Scopes scopeTypes.Scopes |
| paddy@26 | 42 RedirectURI string |
| paddy@26 | 43 State string |
| paddy@69 | 44 ProfileID uuid.ID |
| paddy@94 | 45 Used bool |
| paddy@26 | 46 } |
| paddy@26 | 47 |
| paddy@87 | 48 type authorizationCodeStore interface { |
| paddy@87 | 49 getAuthorizationCode(code string) (AuthorizationCode, error) |
| paddy@87 | 50 saveAuthorizationCode(authCode AuthorizationCode) error |
| paddy@87 | 51 deleteAuthorizationCode(code string) error |
| paddy@163 | 52 deleteAuthorizationCodesByProfileID(profileID uuid.ID) error |
| paddy@164 | 53 deleteAuthorizationCodesByClientID(clientID uuid.ID) error |
| paddy@94 | 54 useAuthorizationCode(code string) error |
| paddy@26 | 55 } |
| paddy@29 | 56 |
| paddy@87 | 57 func (m *memstore) getAuthorizationCode(code string) (AuthorizationCode, error) { |
| paddy@87 | 58 m.authCodeLock.RLock() |
| paddy@87 | 59 defer m.authCodeLock.RUnlock() |
| paddy@87 | 60 authCode, ok := m.authCodes[code] |
| paddy@29 | 61 if !ok { |
| paddy@87 | 62 return AuthorizationCode{}, ErrAuthorizationCodeNotFound |
| paddy@29 | 63 } |
| paddy@87 | 64 return authCode, nil |
| paddy@29 | 65 } |
| paddy@29 | 66 |
| paddy@87 | 67 func (m *memstore) saveAuthorizationCode(authCode AuthorizationCode) error { |
| paddy@87 | 68 m.authCodeLock.Lock() |
| paddy@87 | 69 defer m.authCodeLock.Unlock() |
| paddy@87 | 70 _, ok := m.authCodes[authCode.Code] |
| paddy@29 | 71 if ok { |
| paddy@87 | 72 return ErrAuthorizationCodeAlreadyExists |
| paddy@29 | 73 } |
| paddy@87 | 74 m.authCodes[authCode.Code] = authCode |
| paddy@29 | 75 return nil |
| paddy@29 | 76 } |
| paddy@29 | 77 |
| paddy@87 | 78 func (m *memstore) deleteAuthorizationCode(code string) error { |
| paddy@87 | 79 m.authCodeLock.Lock() |
| paddy@87 | 80 defer m.authCodeLock.Unlock() |
| paddy@87 | 81 _, ok := m.authCodes[code] |
| paddy@29 | 82 if !ok { |
| paddy@87 | 83 return ErrAuthorizationCodeNotFound |
| paddy@29 | 84 } |
| paddy@87 | 85 delete(m.authCodes, code) |
| paddy@29 | 86 return nil |
| paddy@29 | 87 } |
| paddy@84 | 88 |
| paddy@163 | 89 func (m *memstore) deleteAuthorizationCodesByProfileID(profileID uuid.ID) error { |
| paddy@163 | 90 m.authCodeLock.Lock() |
| paddy@163 | 91 defer m.authCodeLock.Unlock() |
| paddy@163 | 92 var codes []string |
| paddy@163 | 93 for _, code := range m.authCodes { |
| paddy@163 | 94 if code.ProfileID.Equal(profileID) { |
| paddy@163 | 95 codes = append(codes, code.Code) |
| paddy@163 | 96 } |
| paddy@163 | 97 } |
| paddy@163 | 98 if len(codes) < 1 { |
| paddy@163 | 99 return ErrProfileNotFound |
| paddy@163 | 100 } |
| paddy@163 | 101 for _, code := range codes { |
| paddy@163 | 102 delete(m.authCodes, code) |
| paddy@163 | 103 } |
| paddy@163 | 104 return nil |
| paddy@163 | 105 } |
| paddy@163 | 106 |
| paddy@164 | 107 func (m *memstore) deleteAuthorizationCodesByClientID(clientID uuid.ID) error { |
| paddy@164 | 108 m.authCodeLock.Lock() |
| paddy@164 | 109 defer m.authCodeLock.Unlock() |
| paddy@164 | 110 var codes []string |
| paddy@164 | 111 for _, code := range m.authCodes { |
| paddy@164 | 112 if code.ClientID.Equal(clientID) { |
| paddy@164 | 113 codes = append(codes, code.Code) |
| paddy@164 | 114 } |
| paddy@164 | 115 } |
| paddy@164 | 116 if len(codes) < 1 { |
| paddy@164 | 117 return ErrClientNotFound |
| paddy@164 | 118 } |
| paddy@164 | 119 for _, code := range codes { |
| paddy@164 | 120 delete(m.authCodes, code) |
| paddy@164 | 121 } |
| paddy@164 | 122 return nil |
| paddy@164 | 123 } |
| paddy@164 | 124 |
| paddy@94 | 125 func (m *memstore) useAuthorizationCode(code string) error { |
| paddy@94 | 126 m.authCodeLock.Lock() |
| paddy@94 | 127 defer m.authCodeLock.Unlock() |
| paddy@94 | 128 a, ok := m.authCodes[code] |
| paddy@94 | 129 if !ok { |
| paddy@94 | 130 return ErrAuthorizationCodeNotFound |
| paddy@94 | 131 } |
| paddy@94 | 132 a.Used = true |
| paddy@94 | 133 m.authCodes[code] = a |
| paddy@94 | 134 return nil |
| paddy@94 | 135 } |
| paddy@94 | 136 |
| paddy@181 | 137 func authCodeGrantValidate(w http.ResponseWriter, r *http.Request, context Context) (scopes scopeTypes.Scopes, profileID uuid.ID, valid bool) { |
| paddy@84 | 138 enc := json.NewEncoder(w) |
| paddy@84 | 139 code := r.PostFormValue("code") |
| paddy@84 | 140 if code == "" { |
| paddy@84 | 141 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 142 renderJSONError(enc, "invalid_request") |
| paddy@84 | 143 return |
| paddy@84 | 144 } |
| paddy@123 | 145 clientID, _, ok := getClientAuth(w, r, true) |
| paddy@123 | 146 if !ok { |
| paddy@84 | 147 return |
| paddy@84 | 148 } |
| paddy@87 | 149 authCode, err := context.GetAuthorizationCode(code) |
| paddy@84 | 150 if err != nil { |
| paddy@87 | 151 if err == ErrAuthorizationCodeNotFound { |
| paddy@84 | 152 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 153 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 154 return |
| paddy@84 | 155 } |
| paddy@84 | 156 w.WriteHeader(http.StatusInternalServerError) |
| paddy@84 | 157 renderJSONError(enc, "server_error") |
| paddy@84 | 158 return |
| paddy@84 | 159 } |
| paddy@85 | 160 redirectURI := r.PostFormValue("redirect_uri") |
| paddy@87 | 161 if authCode.RedirectURI != redirectURI { |
| paddy@84 | 162 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 163 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 164 return |
| paddy@84 | 165 } |
| paddy@87 | 166 if !authCode.ClientID.Equal(clientID) { |
| paddy@84 | 167 w.WriteHeader(http.StatusBadRequest) |
| paddy@84 | 168 renderJSONError(enc, "invalid_grant") |
| paddy@84 | 169 return |
| paddy@84 | 170 } |
| paddy@135 | 171 return authCode.Scopes, authCode.ProfileID, true |
| paddy@84 | 172 } |
| paddy@90 | 173 |
| paddy@90 | 174 func authCodeGrantInvalidate(r *http.Request, context Context) error { |
| paddy@94 | 175 code := r.PostFormValue("code") |
| paddy@94 | 176 if code == "" { |
| paddy@94 | 177 return ErrAuthorizationCodeNotFound |
| paddy@94 | 178 } |
| paddy@94 | 179 return context.UseAuthorizationCode(code) |
| paddy@90 | 180 } |
| paddy@124 | 181 |
| paddy@124 | 182 func authCodeGrantAuditString(r *http.Request) string { |
| paddy@124 | 183 return "authcode:" + r.PostFormValue("code") |
| paddy@124 | 184 } |