/* Copyright (c) 2009 Yahoo! Inc. All rights reserved. The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license */ package com.yahoo.oauth { import flash.net.URLRequestHeader; import flash.net.URLVariables; /** * The OAuthRequest is used to create a request and apply a signature. * * @author Zach Graves (zachg@yahoo-inc.com) * @see http://oauth.net/core/1.0 */ public class OAuthRequest { /** * A string which is used in buildRequest that the * returned data type should be a url encoded string. */ public static const OAUTH_REQUEST_TYPE_URL_STRING:String = "OAUTH_REQUEST_TYPE_URL_STRING"; /** * A string which is used in buildRequest that the * returned data type should be an URLVariables object. */ public static const OAUTH_REQUEST_TYPE_URL_VARIABLES:String = "OAUTH_REQUEST_TYPE_URL_VARIABLES"; /** * A string which is used in buildRequest that the * returned data type should be a string, usable as POST data. */ public static const OAUTH_REQUEST_TYPE_POST:String = "OAUTH_REQUEST_TYPE_POST"; /** * A string which is used in buildRequest that the * returned data type should be an URLRequestHeader type, containing * an OAuth Authorization header. * * @see http://oauth.net/core/1.0#auth_header */ public static const OAUTH_REQUEST_TYPE_HEADER:String = "OAUTH_REQUEST_TYPE_HEADER"; /** * A string which is used in buildRequest that the * returned data type should be an Object. */ public static const OAUTH_REQUEST_TYPE_OBJECT:String = "OAUTH_REQUEST_TYPE_OBJECT"; /** * @private */ private var $consumer:OAuthConsumer; /** * @private */ private var $httpMethod:String; /** * @private */ private var $requestURL:String; /** * @private */ private var $requestParams:Object; /** * @private */ private var $token:OAuthToken; /** * Determines if the request parameters in the signature base string should be encoded using encodeURIComponent. */ public var useExplicitEncoding:Boolean = true; /** * Class constructor. * * Creates a new OAuthRequest object. * * @param httpMethod The HTTP request method used to send the request. The value MUST be a member of URLRequestMethod * @param requestURL The URL of the request, it must include the protocol, host, port (optional) and path. * @param requestParams An optional object containing the key=value pairs to be used in the request. * @param consumer An optional OAuthConsumer object used to sign the request. * @param token An optional OAuthToken object used to sign the request. * * @see flash.net.URLRequestMethod * @see OAuthConsumer * @see OAuthToken * */ public function OAuthRequest(httpMethod:String, requestURL:String, requestParams:Object=null, consumer:OAuthConsumer=null, token:OAuthToken=null) { $httpMethod = httpMethod; $requestURL = requestURL; $requestParams = (requestParams != null) ? OAuthUtil.copyObject(requestParams) : {}; $consumer = consumer; $token = token; } //------------------------- // Public Properties //------------------------- /** * The HTTP request method used to send the request. Value MUST be a member of URLRequestMethod * @see flash.net.URLRequestMethod */ public function get httpMethod():String { return $httpMethod; } /** * @private */ public function set httpMethod(value:String):void { if (value != $httpMethod) { $httpMethod = value; } } /** * The URL of the resource. * * The request URL MUST include scheme, authority, and path and must exclude the query string in * favor of the requestParams object. * * @see http://oauth.net/core/1.0#request_urls */ public function get requestURL():String { return $requestURL; } /** * @private */ public function set requestURL(value:String):void { if (value != $requestURL) { $requestURL = value; } } /** * An object containing key=value pairs used in the request and signing. * * OAuth Protocol Parameter names and values are case sensitive. * * Each OAuth Protocol Parameters must not appear more than once per request, * are required unless otherwise noted. * * @see http://oauth.net/core/1.0#anchor7 */ public function get requestParams():Object { return $requestParams; } /** * @private */ public function set requestParams(value:Object):void { if (value != $requestParams) { $requestParams = value; } } /** * The OAuth consumer. * * @see OAuthConsumer */ public function get consumer():OAuthConsumer { return $consumer; } /** * @private */ public function set consumer(value:OAuthConsumer):void { $consumer = value; } /** * The OAuth access token. * @see OAuthToken */ public function get token():OAuthToken { return $token; } /** * @private */ public function set token(value:OAuthToken):void { $token = value; } /** * An array of the valid request types used to build and sign a request. * @return * */ public function get validRequestTypes():Array { return [OAUTH_REQUEST_TYPE_URL_STRING, OAUTH_REQUEST_TYPE_URL_VARIABLES, OAUTH_REQUEST_TYPE_POST, OAUTH_REQUEST_TYPE_HEADER, OAUTH_REQUEST_TYPE_OBJECT]; } /** * Returns all non-OAuth parameters. * @return * */ public function getNonOAuthParams():Object { var params:Object = {}; for(var key:String in $requestParams) { if(key.indexOf("oauth_") == -1) params[key] = $requestParams[key]; } return params; } /** * Returns all OAuth parameters. * @return * */ public function getOAuthParams():Object { var params:Object = {}; for(var key:String in $requestParams) { if(key.indexOf("oauth_") == 0) params[key] = $requestParams[key]; } return params; } //------------------------- // Public Functions //------------------------- /** * Builds a signable request string containing the http method, url and parameters. * * @return */ public function getSignableString():String { var signable:Array = [null,null,null]; signable[0] = ( encodeURIComponent( $httpMethod ) ); signable[1] = ( encodeURIComponent( $requestURL ) ); signable[2] = ( encodeURIComponent( getSignableParameters() ) ); return signable.join("&"); } /** * Builds and signs the request using the provided signature method and result type requested. * * @param signatureMethod An object implemented with IOAuthSignature method. Typically OAuthSignatureMethod_PLAINTEXT or OAuthSignatureMethod_HMAC_SHA1. * @param requestType A request type that determines what OAuth signature is returned. * @param realm A string which specifies a realm which will be used in the request header. Used only if the requestType equals OAUTH_REQUEST_TYPE_HEADER. * @return An object whose type is determined by the requestType. * * @see OAuthSignatureMethod_PLAINTEXT * @see OAuthSignatureMethod_HMAC_SHA1 * @see IOAuthSignatureMethod * @throws Error Throws an Error if the signature method or request type is invalid and the request cannot be signed. */ public function buildRequest(signatureMethod:IOAuthSignatureMethod, requestType:String=OAUTH_REQUEST_TYPE_URL_STRING, realm:String=null):* { // check that the signature method is valid if(!signatureMethod.name) throw new Error("OAuth signature method not implemented."); // check that the requestType is valid. if(this.validRequestTypes.indexOf(requestType) == -1) throw new Error("The OAuth request type: "+requestType+", is not implemented. Value must be a property of OAuthRequest::validRequestTypes"); // use the current date for the oauth_timestamp and to generate the nonce. var date:Date = new Date(); // add the required oauth params. $requestParams.oauth_consumer_key = this.$consumer.key; $requestParams.oauth_nonce = "blah";//OAuthUtil.generate_nonce(date); $requestParams.oauth_signature_method = signatureMethod.name; $requestParams.oauth_timestamp = "blah";//OAuthUtil.generate_timestamp(date); $requestParams.oauth_version = OAuthUtil.OAUTH_VERSION; // to support two-legged auth, add the token key only if theres a token present if(this.$token) { $requestParams.oauth_token = this.$token.key; } // generate the signature $requestParams.oauth_signature = encodeURIComponent(signatureMethod.buildSignature(this)); var request:* = null; // build the request object based on the type selected. switch (requestType) { case OAUTH_REQUEST_TYPE_URL_STRING: request = this.buildURLString(); break; case OAUTH_REQUEST_TYPE_URL_VARIABLES: request = this.buildURLVariables(); break; case OAUTH_REQUEST_TYPE_OBJECT: request = this.buildURLObject(); break; case OAUTH_REQUEST_TYPE_POST: request = this.getParameters(); break; case OAUTH_REQUEST_TYPE_HEADER: request = this.buildAuthorizationHeader(realm); break; } return request; } //------------------------- // Private Functions //------------------------- /** * Builds the request parameters as a URLVariables object containing the key=value pairs. * @return * */ private function buildURLVariables():URLVariables { var args:URLVariables = new URLVariables(); for (var param:Object in $requestParams) { args[param] = $requestParams[param]; } return args; } /** * Builds the request parameters as an Object containing key value pairs. * @return * */ private function buildURLObject():Object { var args:Object = {}; var signableParams:Object = OAuthUtil.copyObject($requestParams); for (var param:Object in signableParams) { // fugly if( String(param).indexOf("oauth_") == 0) { $requestParams[param] = encodeURIComponent($requestParams[param]); // trace(param, $requestParams[param]); } args[param] = $requestParams[param]; } return args; } /** * Builds the request parameters as a URL. * * @return A String containing the requestURL and the parameter query string. * */ private function buildURLString():String { return $requestURL+"?"+getParameters(); } /** * Builds the request parameters as an OAuth Authorization header. * * @param realm A string which specifies the OAuth realm. * @return * @private * @see http://oauth.net/core/1.0/#auth_header */ private function buildAuthorizationHeader(realm:String=null):URLRequestHeader { var signableParams:Object = OAuthUtil.copyObject($requestParams); var oauth:String = "realm=\""+realm+"\""; for(var i:String in $requestParams) { if(i.indexOf("oauth") == 0) oauth += ","+i+"=\""+$requestParams[i]+"\""; } return new URLRequestHeader("Authorization", "OAuth "+oauth); } /** * Builds a query string that consists of all the parameters that need to be signed. * * @return * @private */ private function getSignableParameters():String { var signableParameters:Array = []; var temp:Array = []; // be safe, copy the object so we dont attempt to alter the original object. var params:Object = OAuthUtil.copyObject($requestParams); // delete the signature, we dont want it in the sig. delete params.oauth_signature; // urlencode the oauth_token, causes problems sometimes with making valid signatures. if(params.oauth_token) { params.oauth_token = encodeURIComponent(params.oauth_token); } var value:String = null; for (var key:String in params) { key = OAuthUtil.urlencodeRFC3986(key); value = params[key].toString(); if(this.useExplicitEncoding) value = OAuthUtil.urlencodeRFC3986(value); signableParameters.push(key+"="+value); } // oauth expects the parameter string // to be sorted alphabetically by key. // technically, this isn't good // since "=" can mess up the sort signableParameters.sort(); // return a query string. return signableParameters.join("&"); } /** * Builds an encoded query string that consists of all the parameters. * * @return * @private */ private function getParameters():String { var params:Array = []; var value:String = null; // build a key=value string for (var key:String in $requestParams) { value = $requestParams[key].toString(); params.push(key+"="+encodeURIComponent(value)); } // oauth expects the parameter string // to be sorted alphabetically by key. params.sort(); // return as a query string. return params.join("&"); } } }