Differential D15593 Diff 37613 src/applications/conduit/controller/PhabricatorConduitAPIController.php
Changeset View
Changeset View
Standalone View
Standalone View
src/applications/conduit/controller/PhabricatorConduitAPIController.php
Show All 39 Lines | try { | ||||
// the Call and APIRequest is very awkward. | // the Call and APIRequest is very awkward. | ||||
$api_request = $call->getAPIRequest(); | $api_request = $call->getAPIRequest(); | ||||
$allow_unguarded_writes = false; | $allow_unguarded_writes = false; | ||||
$auth_error = null; | $auth_error = null; | ||||
$conduit_username = '-'; | $conduit_username = '-'; | ||||
if ($call->shouldRequireAuthentication()) { | if ($call->shouldRequireAuthentication()) { | ||||
$metadata['scope'] = $call->getRequiredScope(); | |||||
$auth_error = $this->authenticateUser($api_request, $metadata, $method); | $auth_error = $this->authenticateUser($api_request, $metadata, $method); | ||||
// If we've explicitly authenticated the user here and either done | // If we've explicitly authenticated the user here and either done | ||||
// CSRF validation or are using a non-web authentication mechanism. | // CSRF validation or are using a non-web authentication mechanism. | ||||
$allow_unguarded_writes = true; | $allow_unguarded_writes = true; | ||||
if ($auth_error === null) { | if ($auth_error === null) { | ||||
$conduit_user = $api_request->getUser(); | $conduit_user = $api_request->getUser(); | ||||
if ($conduit_user && $conduit_user->getPHID()) { | if ($conduit_user && $conduit_user->getPHID()) { | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | if ($auth_type === ConduitClient::AUTH_ASYMMETRIC) { | ||||
$raw_key = idx($metadata, 'auth.key'); | $raw_key = idx($metadata, 'auth.key'); | ||||
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key); | $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_key); | ||||
$ssl_public_key = $public_key->toPKCS8(); | $ssl_public_key = $public_key->toPKCS8(); | ||||
// First, verify the signature. | // First, verify the signature. | ||||
try { | try { | ||||
$protocol_data = $metadata; | $protocol_data = $metadata; | ||||
// TODO: We should stop writing this into the protocol data when | |||||
// processing a request. | |||||
unset($protocol_data['scope']); | |||||
ConduitClient::verifySignature( | ConduitClient::verifySignature( | ||||
$method, | $method, | ||||
$api_request->getAllParameters(), | $api_request->getAllParameters(), | ||||
$protocol_data, | $protocol_data, | ||||
$ssl_public_key); | $ssl_public_key); | ||||
} catch (Exception $ex) { | } catch (Exception $ex) { | ||||
return array( | return array( | ||||
'ERR-INVALID-AUTH', | 'ERR-INVALID-AUTH', | ||||
▲ Show 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | if (strlen($token_string)) { | ||||
); | ); | ||||
} | } | ||||
return $this->validateAuthenticatedUser( | return $this->validateAuthenticatedUser( | ||||
$api_request, | $api_request, | ||||
$user); | $user); | ||||
} | } | ||||
// handle oauth | |||||
$access_token = idx($metadata, 'access_token'); | $access_token = idx($metadata, 'access_token'); | ||||
$method_scope = idx($metadata, 'scope'); | if ($access_token) { | ||||
if ($access_token && | |||||
$method_scope != PhabricatorOAuthServerScope::SCOPE_NOT_ACCESSIBLE) { | |||||
$token = id(new PhabricatorOAuthServerAccessToken()) | $token = id(new PhabricatorOAuthServerAccessToken()) | ||||
->loadOneWhere('token = %s', $access_token); | ->loadOneWhere('token = %s', $access_token); | ||||
if (!$token) { | if (!$token) { | ||||
return array( | return array( | ||||
'ERR-INVALID-AUTH', | 'ERR-INVALID-AUTH', | ||||
pht('Access token does not exist.'), | pht('Access token does not exist.'), | ||||
); | ); | ||||
} | } | ||||
$oauth_server = new PhabricatorOAuthServer(); | $oauth_server = new PhabricatorOAuthServer(); | ||||
$valid = $oauth_server->validateAccessToken($token, | $authorization = $oauth_server->authorizeToken($token); | ||||
$method_scope); | if (!$authorization) { | ||||
if (!$valid) { | |||||
return array( | return array( | ||||
'ERR-INVALID-AUTH', | 'ERR-INVALID-AUTH', | ||||
pht('Access token is invalid.'), | pht('Access token is invalid or expired.'), | ||||
); | ); | ||||
} | } | ||||
// valid token, so let's log in the user! | $user = id(new PhabricatorPeopleQuery()) | ||||
$user_phid = $token->getUserPHID(); | ->setViewer(PhabricatorUser::getOmnipotentUser()) | ||||
$user = id(new PhabricatorUser()) | ->withPHIDs(array($token->getUserPHID())) | ||||
->loadOneWhere('phid = %s', $user_phid); | ->executeOne(); | ||||
if (!$user) { | if (!$user) { | ||||
return array( | return array( | ||||
'ERR-INVALID-AUTH', | 'ERR-INVALID-AUTH', | ||||
pht('Access token is for invalid user.'), | pht('Access token is for invalid user.'), | ||||
); | ); | ||||
} | } | ||||
$ok = $this->authorizeOAuthMethodAccess($authorization, $method); | |||||
if (!$ok) { | |||||
return array( | |||||
'ERR-OAUTH-ACCESS', | |||||
pht('You do not have authorization to call this method.'), | |||||
); | |||||
} | |||||
return $this->validateAuthenticatedUser( | return $this->validateAuthenticatedUser( | ||||
$api_request, | $api_request, | ||||
$user); | $user); | ||||
} | } | ||||
// Handle sessionless auth. | // Handle sessionless auth. | ||||
// TODO: This is super messy. | // TODO: This is super messy. | ||||
// TODO: Remove this in favor of token-based auth. | // TODO: Remove this in favor of token-based auth. | ||||
▲ Show 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | foreach ($request->getPassthroughRequestData() as $key => $value) { | ||||
} else { | } else { | ||||
$params[$key] = $value; | $params[$key] = $value; | ||||
} | } | ||||
} | } | ||||
return array($metadata, $params); | return array($metadata, $params); | ||||
} | } | ||||
private function authorizeOAuthMethodAccess( | |||||
PhabricatorOAuthClientAuthorization $authorization, | |||||
$method_name) { | |||||
$method = ConduitAPIMethod::getConduitMethod($method_name); | |||||
if (!$method) { | |||||
return false; | |||||
} | |||||
$required_scope = $method->getRequiredScope(); | |||||
switch ($required_scope) { | |||||
case ConduitAPIMethod::SCOPE_ALWAYS: | |||||
return true; | |||||
case ConduitAPIMethod::SCOPE_NEVER: | |||||
return false; | |||||
} | |||||
$authorization_scope = $authorization->getScope(); | |||||
if (!empty($authorization_scope[$required_scope])) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} | } |