[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Twilio Capability Token generator 5 * 6 * @category Services 7 * @package Services_Twilio 8 * @author Jeff Lindsay <[email protected]> 9 * @license http://creativecommons.org/licenses/MIT/ MIT 10 */ 11 class Services_Twilio_Capability 12 { 13 public $accountSid; 14 public $authToken; 15 public $scopes; 16 17 /** 18 * Create a new TwilioCapability with zero permissions. Next steps are to 19 * grant access to resources by configuring this token through the 20 * functions allowXXXX. 21 * 22 * @param $accountSid the account sid to which this token is granted access 23 * @param $authToken the secret key used to sign the token. Note, this auth 24 * token is not visible to the user of the token. 25 */ 26 public function __construct($accountSid, $authToken) 27 { 28 $this->accountSid = $accountSid; 29 $this->authToken = $authToken; 30 $this->scopes = array(); 31 $this->clientName = false; 32 } 33 34 /** 35 * If the user of this token should be allowed to accept incoming 36 * connections then configure the TwilioCapability through this method and 37 * specify the client name. 38 * 39 * @param $clientName 40 */ 41 public function allowClientIncoming($clientName) 42 { 43 44 // clientName must be a non-zero length alphanumeric string 45 if (preg_match('/\W/', $clientName)) { 46 throw new InvalidArgumentException( 47 'Only alphanumeric characters allowed in client name.'); 48 } 49 50 if (strlen($clientName) == 0) { 51 throw new InvalidArgumentException( 52 'Client name must not be a zero length string.'); 53 } 54 55 $this->clientName = $clientName; 56 $this->allow('client', 'incoming', 57 array('clientName' => $clientName)); 58 } 59 60 /** 61 * Allow the user of this token to make outgoing connections. 62 * 63 * @param $appSid the application to which this token grants access 64 * @param $appParams signed parameters that the user of this token cannot 65 * overwrite. 66 */ 67 public function allowClientOutgoing($appSid, array $appParams=array()) 68 { 69 $this->allow('client', 'outgoing', array( 70 'appSid' => $appSid, 71 'appParams' => http_build_query($appParams, '', '&'))); 72 } 73 74 /** 75 * Allow the user of this token to access their event stream. 76 * 77 * @param $filters key/value filters to apply to the event stream 78 */ 79 public function allowEventStream(array $filters=array()) 80 { 81 $this->allow('stream', 'subscribe', array( 82 'path' => '/2010-04-01/Events', 83 'params' => http_build_query($filters, '', '&'), 84 )); 85 } 86 87 /** 88 * Generates a new token based on the credentials and permissions that 89 * previously has been granted to this token. 90 * 91 * @param $ttl the expiration time of the token (in seconds). Default 92 * value is 3600 (1hr) 93 * @return the newly generated token that is valid for $ttl seconds 94 */ 95 public function generateToken($ttl = 3600) 96 { 97 $payload = array( 98 'scope' => array(), 99 'iss' => $this->accountSid, 100 'exp' => time() + $ttl, 101 ); 102 $scopeStrings = array(); 103 104 foreach ($this->scopes as $scope) { 105 if ($scope->privilege == "outgoing" && $this->clientName) 106 $scope->params["clientName"] = $this->clientName; 107 $scopeStrings[] = $scope->toString(); 108 } 109 110 $payload['scope'] = implode(' ', $scopeStrings); 111 return JWT::encode($payload, $this->authToken, 'HS256'); 112 } 113 114 protected function allow($service, $privilege, $params) { 115 $this->scopes[] = new ScopeURI($service, $privilege, $params); 116 } 117 } 118 119 /** 120 * Scope URI implementation 121 * 122 * Simple way to represent configurable privileges in an OAuth 123 * friendly way. For our case, they look like this: 124 * 125 * scope:<service>:<privilege>?<params> 126 * 127 * For example: 128 * scope:client:incoming?name=jonas 129 * 130 * @author Jeff Lindsay <[email protected]> 131 */ 132 class ScopeURI 133 { 134 public $service; 135 public $privilege; 136 public $params; 137 138 public function __construct($service, $privilege, $params = array()) 139 { 140 $this->service = $service; 141 $this->privilege = $privilege; 142 $this->params = $params; 143 } 144 145 public function toString() 146 { 147 $uri = "scope:{$this->service}:{$this->privilege}"; 148 if (count($this->params)) { 149 $uri .= "?".http_build_query($this->params, '', '&'); 150 } 151 return $uri; 152 } 153 154 /** 155 * Parse a scope URI into a ScopeURI object 156 * 157 * @param string $uri The scope URI 158 * @return ScopeURI The parsed scope uri 159 */ 160 public static function parse($uri) 161 { 162 if (strpos($uri, 'scope:') !== 0) { 163 throw new UnexpectedValueException( 164 'Not a scope URI according to scheme'); 165 } 166 167 $parts = explode('?', $uri, 1); 168 $params = null; 169 170 if (count($parts) > 1) { 171 parse_str($parts[1], $params); 172 } 173 174 $parts = explode(':', $parts[0], 2); 175 176 if (count($parts) != 3) { 177 throw new UnexpectedValueException( 178 'Not enough parts for scope URI'); 179 } 180 181 list($scheme, $service, $privilege) = $parts; 182 return new ScopeURI($service, $privilege, $params); 183 } 184 185 } 186 187 /** 188 * JSON Web Token implementation 189 * 190 * Minimum implementation used by Realtime auth, based on this spec: 191 * http://self-issued.info/docs/draft-jones-json-web-token-01.html. 192 * 193 * @author Neuman Vong <[email protected]> 194 */ 195 class JWT 196 { 197 /** 198 * @param string $jwt The JWT 199 * @param string|null $key The secret key 200 * @param bool $verify Don't skip verification process 201 * 202 * @return object The JWT's payload as a PHP object 203 */ 204 public static function decode($jwt, $key = null, $verify = true) 205 { 206 $tks = explode('.', $jwt); 207 if (count($tks) != 3) { 208 throw new UnexpectedValueException('Wrong number of segments'); 209 } 210 list($headb64, $payloadb64, $cryptob64) = $tks; 211 if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64))) 212 ) { 213 throw new UnexpectedValueException('Invalid segment encoding'); 214 } 215 if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($payloadb64)) 216 ) { 217 throw new UnexpectedValueException('Invalid segment encoding'); 218 } 219 $sig = JWT::urlsafeB64Decode($cryptob64); 220 if ($verify) { 221 if (empty($header->alg)) { 222 throw new DomainException('Empty algorithm'); 223 } 224 if ($sig != JWT::sign("$headb64.$payloadb64", $key, $header->alg)) { 225 throw new UnexpectedValueException('Signature verification failed'); 226 } 227 } 228 return $payload; 229 } 230 231 /** 232 * @param object|array $payload PHP object or array 233 * @param string $key The secret key 234 * @param string $algo The signing algorithm 235 * 236 * @return string A JWT 237 */ 238 public static function encode($payload, $key, $algo = 'HS256') 239 { 240 $header = array('typ' => 'JWT', 'alg' => $algo); 241 242 $segments = array(); 243 $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); 244 $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); 245 $signing_input = implode('.', $segments); 246 247 $signature = JWT::sign($signing_input, $key, $algo); 248 $segments[] = JWT::urlsafeB64Encode($signature); 249 250 return implode('.', $segments); 251 } 252 253 /** 254 * @param string $msg The message to sign 255 * @param string $key The secret key 256 * @param string $method The signing algorithm 257 * 258 * @return string An encrypted message 259 */ 260 public static function sign($msg, $key, $method = 'HS256') 261 { 262 $methods = array( 263 'HS256' => 'sha256', 264 'HS384' => 'sha384', 265 'HS512' => 'sha512', 266 ); 267 if (empty($methods[$method])) { 268 throw new DomainException('Algorithm not supported'); 269 } 270 return hash_hmac($methods[$method], $msg, $key, true); 271 } 272 273 /** 274 * @param string $input JSON string 275 * 276 * @return object Object representation of JSON string 277 */ 278 public static function jsonDecode($input) 279 { 280 $obj = json_decode($input); 281 if (function_exists('json_last_error') && $errno = json_last_error()) { 282 JWT::handleJsonError($errno); 283 } 284 else if ($obj === null && $input !== 'null') { 285 throw new DomainException('Null result with non-null input'); 286 } 287 return $obj; 288 } 289 290 /** 291 * @param object|array $input A PHP object or array 292 * 293 * @return string JSON representation of the PHP object or array 294 */ 295 public static function jsonEncode($input) 296 { 297 $json = json_encode($input); 298 if (function_exists('json_last_error') && $errno = json_last_error()) { 299 JWT::handleJsonError($errno); 300 } 301 else if ($json === 'null' && $input !== null) { 302 throw new DomainException('Null result with non-null input'); 303 } 304 return $json; 305 } 306 307 /** 308 * @param string $input A base64 encoded string 309 * 310 * @return string A decoded string 311 */ 312 public static function urlsafeB64Decode($input) 313 { 314 $padlen = 4 - strlen($input) % 4; 315 $input .= str_repeat('=', $padlen); 316 return base64_decode(strtr($input, '-_', '+/')); 317 } 318 319 /** 320 * @param string $input Anything really 321 * 322 * @return string The base64 encode of what you passed in 323 */ 324 public static function urlsafeB64Encode($input) 325 { 326 return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 327 } 328 329 /** 330 * @param int $errno An error number from json_last_error() 331 * 332 * @return void 333 */ 334 private static function handleJsonError($errno) 335 { 336 $messages = array( 337 JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 338 JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 339 JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 340 ); 341 throw new DomainException(isset($messages[$errno]) 342 ? $messages[$errno] 343 : 'Unknown JSON error: ' . $errno 344 ); 345 } 346 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |