Generated on 2009-08-09 21:22:56 with etap 0.3.4.
| Name | Total lines | Lines of code | Total coverage | Code coverage | ||||
| couch_httpd_oauth | ?? | ?? | ?? |
|
....1 % Licensed under the Apache License, Version 2.0 (the "License"); you may not ....2 % use this file except in compliance with the License. You may obtain a copy of ....3 % the License at ....4 % ....5 % http://www.apache.org/licenses/LICENSE-2.0 ....6 % ....7 % Unless required by applicable law or agreed to in writing, software ....8 % distributed under the License is distributed on an "AS IS" BASIS, WITHOUT ....9 % WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the ...10 % License for the specific language governing permissions and limitations under ...11 % the License. ...12 ...13 -module(couch_httpd_oauth). ...14 -include("couch_db.hrl"). ...15 ...16 -export([oauth_authentication_handler/1, handle_oauth_req/1, consumer_lookup/2]). ...17 ...18 % OAuth auth handler using per-node user db ...19 oauth_authentication_handler(#httpd{method=Method}=Req) -> ...20 serve_oauth(Req, fun(URL, Params, Consumer, Signature) -> ...21 AccessToken = proplists:get_value("oauth_token", Params), ...22 TokenSecret = couch_config:get("oauth_token_secrets", AccessToken), ...23 case oauth:verify(Signature, atom_to_list(Method), URL, Params, Consumer, TokenSecret) of ...24 true -> ...25 set_user_ctx(Req, AccessToken); ...26 false -> ...27 Req ...28 end ...29 end, true). ...30 ...31 % Look up the consumer key and get the roles to give the consumer ...32 set_user_ctx(Req, AccessToken) -> ...33 DbName = couch_config:get("couch_httpd_auth", "authentication_db"), ...34 couch_httpd_auth:ensure_users_db_exists(?l2b(DbName)), ...35 case couch_db:open(?l2b(DbName), [{user_ctx, #user_ctx{roles=[<<"_admin">>]}}]) of ...36 {ok, Db} -> ...37 Name = ?l2b(couch_config:get("oauth_token_users", AccessToken)), ...38 case couch_httpd_auth:get_user(Db, Name) of ...39 nil -> Req; ...40 User -> ...41 Roles = proplists:get_value(<<"roles">>, User, []), ...42 Req#httpd{user_ctx=#user_ctx{name=Name, roles=Roles}} ...43 end; ...44 _Else-> ...45 Req ...46 end. ...47 ...48 % OAuth request_token ...49 handle_oauth_req(#httpd{path_parts=[_OAuth, <<"request_token">>], method=Method}=Req) -> ...50 serve_oauth(Req, fun(URL, Params, Consumer, Signature) -> ...51 AccessToken = proplists:get_value("oauth_token", Params), ...52 TokenSecret = couch_config:get("oauth_token_secrets", AccessToken), ...53 case oauth:verify(Signature, atom_to_list(Method), URL, Params, Consumer, TokenSecret) of ...54 true -> ...55 ok(Req, <<"oauth_token=requestkey&oauth_token_secret=requestsecret">>); ...56 false -> ...57 invalid_signature(Req) ...58 end ...59 end, false); ...60 handle_oauth_req(#httpd{path_parts=[_OAuth, <<"authorize">>]}=Req) -> ...61 {ok, serve_oauth_authorize(Req)}; ...62 handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>], method='GET'}=Req) -> ...63 serve_oauth(Req, fun(URL, Params, Consumer, Signature) -> ...64 case oauth:token(Params) of ...65 "requestkey" -> ...66 case oauth:verify(Signature, "GET", URL, Params, Consumer, "requestsecret") of ...67 true -> ...68 ok(Req, <<"oauth_token=accesskey&oauth_token_secret=accesssecret">>); ...69 false -> ...70 invalid_signature(Req) ...71 end; ...72 _ -> ...73 couch_httpd:send_error(Req, 400, <<"invalid_token">>, <<"Invalid OAuth token.">>) ...74 end ...75 end, false); ...76 handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>]}=Req) -> ...77 couch_httpd:send_method_not_allowed(Req, "GET"). ...78 ...79 invalid_signature(Req) -> ...80 couch_httpd:send_error(Req, 400, <<"invalid_signature">>, <<"Invalid signature value.">>). ...81 ...82 % This needs to be protected i.e. force user to login using HTTP Basic Auth or form-based login. ...83 serve_oauth_authorize(#httpd{method=Method}=Req) -> ...84 case Method of ...85 'GET' -> ...86 % Confirm with the User that they want to authenticate the Consumer ...87 serve_oauth(Req, fun(URL, Params, Consumer, Signature) -> ...88 AccessToken = proplists:get_value("oauth_token", Params), ...89 TokenSecret = couch_config:get("oauth_token_secrets", AccessToken), ...90 case oauth:verify(Signature, "GET", URL, Params, Consumer, TokenSecret) of ...91 true -> ...92 ok(Req, <<"oauth_token=requestkey&oauth_token_secret=requestsecret">>); ...93 false -> ...94 invalid_signature(Req) ...95 end ...96 end, false); ...97 'POST' -> ...98 % If the User has confirmed, we direct the User back to the Consumer with a verification code ...99 serve_oauth(Req, fun(URL, Params, Consumer, Signature) -> ..100 AccessToken = proplists:get_value("oauth_token", Params), ..101 TokenSecret = couch_config:get("oauth_token_secrets", AccessToken), ..102 case oauth:verify(Signature, "POST", URL, Params, Consumer, TokenSecret) of ..103 true -> ..104 %redirect(oauth_callback, oauth_token, oauth_verifier), ..105 ok(Req, <<"oauth_token=requestkey&oauth_token_secret=requestsecret">>); ..106 false -> ..107 invalid_signature(Req) ..108 end ..109 end, false); ..110 _ -> ..111 couch_httpd:send_method_not_allowed(Req, "GET,POST") ..112 end. ..113 ..114 serve_oauth(#httpd{mochi_req=MochiReq}=Req, Fun, FailSilently) -> ..115 % 1. In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme. ..116 % 2. As the HTTP POST request body with a content-type of application/x-www-form-urlencoded. ..117 % 3. Added to the URLs in the query part (as defined by [RFC3986] section 3). ..118 AuthHeader = case MochiReq:get_header_value("authorization") of ..119 undefined -> ..120 ""; ..121 Else -> ..122 [Head | Tail] = re:split(Else, "\\s", [{parts, 2}, {return, list}]), ..123 case [string:to_lower(Head) | Tail] of ..124 ["oauth", Rest] -> Rest; ..125 _ -> "" ..126 end ..127 end, ..128 HeaderParams = oauth_uri:params_from_header_string(AuthHeader), ..129 %Realm = proplists:get_value("realm", HeaderParams), ..130 Params = proplists:delete("realm", HeaderParams) ++ MochiReq:parse_qs(), ..131 ?LOG_DEBUG("OAuth Params: ~p", [Params]), ..132 case proplists:get_value("oauth_version", Params, "1.0") of ..133 "1.0" -> ..134 case proplists:get_value("oauth_consumer_key", Params, undefined) of ..135 undefined -> ..136 case FailSilently of ..137 true -> Req; ..138 false -> couch_httpd:send_error(Req, 400, <<"invalid_consumer">>, <<"Invalid consumer.">>) ..139 end; ..140 ConsumerKey -> ..141 SigMethod = proplists:get_value("oauth_signature_method", Params), ..142 case consumer_lookup(ConsumerKey, SigMethod) of ..143 none -> ..144 couch_httpd:send_error(Req, 400, <<"invalid_consumer">>, <<"Invalid consumer (key or signature method).">>); ..145 Consumer -> ..146 Signature = proplists:get_value("oauth_signature", Params), ..147 URL = couch_httpd:absolute_uri(Req, MochiReq:get(path)), ..148 Fun(URL, proplists:delete("oauth_signature", Params), ..149 Consumer, Signature) ..150 end ..151 end; ..152 _ -> ..153 couch_httpd:send_error(Req, 400, <<"invalid_oauth_version">>, <<"Invalid OAuth version.">>) ..154 end. ..155 ..156 consumer_lookup(Key, MethodStr) -> ..157 SignatureMethod = case MethodStr of ..158 "PLAINTEXT" -> plaintext; ..159 "HMAC-SHA1" -> hmac_sha1; ..160 %"RSA-SHA1" -> rsa_sha1; ..161 _Else -> undefined ..162 end, ..163 case SignatureMethod of ..164 undefined -> none; ..165 _SupportedMethod -> ..166 case couch_config:get("oauth_consumer_secrets", Key, undefined) of ..167 undefined -> none; ..168 Secret -> {Key, Secret, SignatureMethod} ..169 end ..170 end. ..171 ..172 ok(#httpd{mochi_req=MochiReq}, Body) -> ..173 {ok, MochiReq:respond({200, [], Body})}.
Generated using etap 0.3.4.