Mongo.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. <?php
  2. namespace OAuth2\Storage;
  3. use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
  4. /**
  5. * Simple MongoDB storage for all storage types
  6. *
  7. * NOTE: This class is meant to get users started
  8. * quickly. If your application requires further
  9. * customization, extend this class or create your own.
  10. *
  11. * NOTE: Passwords are stored in plaintext, which is never
  12. * a good idea. Be sure to override this for your application
  13. *
  14. * @author Julien Chaumond <chaumond@gmail.com>
  15. */
  16. class Mongo implements AuthorizationCodeInterface,
  17. AccessTokenInterface,
  18. ClientCredentialsInterface,
  19. UserCredentialsInterface,
  20. RefreshTokenInterface,
  21. JwtBearerInterface,
  22. PublicKeyInterface,
  23. OpenIDAuthorizationCodeInterface
  24. {
  25. protected $db;
  26. protected $config;
  27. public function __construct($connection, $config = array())
  28. {
  29. if ($connection instanceof \MongoDB) {
  30. $this->db = $connection;
  31. } else {
  32. if (!is_array($connection)) {
  33. throw new \InvalidArgumentException('First argument to OAuth2\Storage\Mongo must be an instance of MongoDB or a configuration array');
  34. }
  35. $server = sprintf('mongodb://%s:%d', $connection['host'], $connection['port']);
  36. $m = new \MongoClient($server);
  37. $this->db = $m->{$connection['database']};
  38. }
  39. $this->config = array_merge(array(
  40. 'client_table' => 'oauth_clients',
  41. 'access_token_table' => 'oauth_access_tokens',
  42. 'refresh_token_table' => 'oauth_refresh_tokens',
  43. 'code_table' => 'oauth_authorization_codes',
  44. 'user_table' => 'oauth_users',
  45. 'key_table' => 'oauth_keys',
  46. 'jwt_table' => 'oauth_jwt',
  47. ), $config);
  48. }
  49. // Helper function to access a MongoDB collection by `type`:
  50. protected function collection($name)
  51. {
  52. return $this->db->{$this->config[$name]};
  53. }
  54. /* ClientCredentialsInterface */
  55. public function checkClientCredentials($client_id, $client_secret = null)
  56. {
  57. if ($result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) {
  58. return $result['client_secret'] == $client_secret;
  59. }
  60. return false;
  61. }
  62. public function isPublicClient($client_id)
  63. {
  64. if (!$result = $this->collection('client_table')->findOne(array('client_id' => $client_id))) {
  65. return false;
  66. }
  67. return empty($result['client_secret']);
  68. }
  69. /* ClientInterface */
  70. public function getClientDetails($client_id)
  71. {
  72. $result = $this->collection('client_table')->findOne(array('client_id' => $client_id));
  73. return is_null($result) ? false : $result;
  74. }
  75. public function setClientDetails($client_id, $client_secret = null, $redirect_uri = null, $grant_types = null, $scope = null, $user_id = null)
  76. {
  77. if ($this->getClientDetails($client_id)) {
  78. $this->collection('client_table')->update(
  79. array('client_id' => $client_id),
  80. array('$set' => array(
  81. 'client_secret' => $client_secret,
  82. 'redirect_uri' => $redirect_uri,
  83. 'grant_types' => $grant_types,
  84. 'scope' => $scope,
  85. 'user_id' => $user_id,
  86. ))
  87. );
  88. } else {
  89. $client = array(
  90. 'client_id' => $client_id,
  91. 'client_secret' => $client_secret,
  92. 'redirect_uri' => $redirect_uri,
  93. 'grant_types' => $grant_types,
  94. 'scope' => $scope,
  95. 'user_id' => $user_id,
  96. );
  97. $this->collection('client_table')->insert($client);
  98. }
  99. return true;
  100. }
  101. public function checkRestrictedGrantType($client_id, $grant_type)
  102. {
  103. $details = $this->getClientDetails($client_id);
  104. if (isset($details['grant_types'])) {
  105. $grant_types = explode(' ', $details['grant_types']);
  106. return in_array($grant_type, $grant_types);
  107. }
  108. // if grant_types are not defined, then none are restricted
  109. return true;
  110. }
  111. /* AccessTokenInterface */
  112. public function getAccessToken($access_token)
  113. {
  114. $token = $this->collection('access_token_table')->findOne(array('access_token' => $access_token));
  115. return is_null($token) ? false : $token;
  116. }
  117. public function setAccessToken($access_token, $client_id, $user_id, $expires, $scope = null)
  118. {
  119. // if it exists, update it.
  120. if ($this->getAccessToken($access_token)) {
  121. $this->collection('access_token_table')->update(
  122. array('access_token' => $access_token),
  123. array('$set' => array(
  124. 'client_id' => $client_id,
  125. 'expires' => $expires,
  126. 'user_id' => $user_id,
  127. 'scope' => $scope
  128. ))
  129. );
  130. } else {
  131. $token = array(
  132. 'access_token' => $access_token,
  133. 'client_id' => $client_id,
  134. 'expires' => $expires,
  135. 'user_id' => $user_id,
  136. 'scope' => $scope
  137. );
  138. $this->collection('access_token_table')->insert($token);
  139. }
  140. return true;
  141. }
  142. public function unsetAccessToken($access_token)
  143. {
  144. $result = $this->collection('access_token_table')->remove(array(
  145. 'access_token' => $access_token
  146. ), array('w' => 1));
  147. return $result['n'] > 0;
  148. }
  149. /* AuthorizationCodeInterface */
  150. public function getAuthorizationCode($code)
  151. {
  152. $code = $this->collection('code_table')->findOne(array('authorization_code' => $code));
  153. return is_null($code) ? false : $code;
  154. }
  155. public function setAuthorizationCode($code, $client_id, $user_id, $redirect_uri, $expires, $scope = null, $id_token = null)
  156. {
  157. // if it exists, update it.
  158. if ($this->getAuthorizationCode($code)) {
  159. $this->collection('code_table')->update(
  160. array('authorization_code' => $code),
  161. array('$set' => array(
  162. 'client_id' => $client_id,
  163. 'user_id' => $user_id,
  164. 'redirect_uri' => $redirect_uri,
  165. 'expires' => $expires,
  166. 'scope' => $scope,
  167. 'id_token' => $id_token,
  168. ))
  169. );
  170. } else {
  171. $token = array(
  172. 'authorization_code' => $code,
  173. 'client_id' => $client_id,
  174. 'user_id' => $user_id,
  175. 'redirect_uri' => $redirect_uri,
  176. 'expires' => $expires,
  177. 'scope' => $scope,
  178. 'id_token' => $id_token,
  179. );
  180. $this->collection('code_table')->insert($token);
  181. }
  182. return true;
  183. }
  184. public function expireAuthorizationCode($code)
  185. {
  186. $this->collection('code_table')->remove(array('authorization_code' => $code));
  187. return true;
  188. }
  189. /* UserCredentialsInterface */
  190. public function checkUserCredentials($username, $password)
  191. {
  192. if ($user = $this->getUser($username)) {
  193. return $this->checkPassword($user, $password);
  194. }
  195. return false;
  196. }
  197. public function getUserDetails($username)
  198. {
  199. if ($user = $this->getUser($username)) {
  200. $user['user_id'] = $user['username'];
  201. }
  202. return $user;
  203. }
  204. /* RefreshTokenInterface */
  205. public function getRefreshToken($refresh_token)
  206. {
  207. $token = $this->collection('refresh_token_table')->findOne(array('refresh_token' => $refresh_token));
  208. return is_null($token) ? false : $token;
  209. }
  210. public function setRefreshToken($refresh_token, $client_id, $user_id, $expires, $scope = null)
  211. {
  212. $token = array(
  213. 'refresh_token' => $refresh_token,
  214. 'client_id' => $client_id,
  215. 'user_id' => $user_id,
  216. 'expires' => $expires,
  217. 'scope' => $scope
  218. );
  219. $this->collection('refresh_token_table')->insert($token);
  220. return true;
  221. }
  222. public function unsetRefreshToken($refresh_token)
  223. {
  224. $result = $this->collection('refresh_token_table')->remove(array(
  225. 'refresh_token' => $refresh_token
  226. ), array('w' => 1));
  227. return $result['n'] > 0;
  228. }
  229. // plaintext passwords are bad! Override this for your application
  230. protected function checkPassword($user, $password)
  231. {
  232. return $user['password'] == $password;
  233. }
  234. public function getUser($username)
  235. {
  236. $result = $this->collection('user_table')->findOne(array('username' => $username));
  237. return is_null($result) ? false : $result;
  238. }
  239. public function setUser($username, $password, $firstName = null, $lastName = null)
  240. {
  241. if ($this->getUser($username)) {
  242. $this->collection('user_table')->update(
  243. array('username' => $username),
  244. array('$set' => array(
  245. 'password' => $password,
  246. 'first_name' => $firstName,
  247. 'last_name' => $lastName
  248. ))
  249. );
  250. } else {
  251. $user = array(
  252. 'username' => $username,
  253. 'password' => $password,
  254. 'first_name' => $firstName,
  255. 'last_name' => $lastName
  256. );
  257. $this->collection('user_table')->insert($user);
  258. }
  259. return true;
  260. }
  261. public function getClientKey($client_id, $subject)
  262. {
  263. $result = $this->collection('jwt_table')->findOne(array(
  264. 'client_id' => $client_id,
  265. 'subject' => $subject
  266. ));
  267. return is_null($result) ? false : $result['key'];
  268. }
  269. public function getClientScope($client_id)
  270. {
  271. if (!$clientDetails = $this->getClientDetails($client_id)) {
  272. return false;
  273. }
  274. if (isset($clientDetails['scope'])) {
  275. return $clientDetails['scope'];
  276. }
  277. return null;
  278. }
  279. public function getJti($client_id, $subject, $audience, $expiration, $jti)
  280. {
  281. //TODO: Needs mongodb implementation.
  282. throw new \Exception('getJti() for the MongoDB driver is currently unimplemented.');
  283. }
  284. public function setJti($client_id, $subject, $audience, $expiration, $jti)
  285. {
  286. //TODO: Needs mongodb implementation.
  287. throw new \Exception('setJti() for the MongoDB driver is currently unimplemented.');
  288. }
  289. public function getPublicKey($client_id = null)
  290. {
  291. if ($client_id) {
  292. $result = $this->collection('key_table')->findOne(array(
  293. 'client_id' => $client_id
  294. ));
  295. if ($result) {
  296. return $result['public_key'];
  297. }
  298. }
  299. $result = $this->collection('key_table')->findOne(array(
  300. 'client_id' => null
  301. ));
  302. return is_null($result) ? false : $result['public_key'];
  303. }
  304. public function getPrivateKey($client_id = null)
  305. {
  306. if ($client_id) {
  307. $result = $this->collection('key_table')->findOne(array(
  308. 'client_id' => $client_id
  309. ));
  310. if ($result) {
  311. return $result['private_key'];
  312. }
  313. }
  314. $result = $this->collection('key_table')->findOne(array(
  315. 'client_id' => null
  316. ));
  317. return is_null($result) ? false : $result['private_key'];
  318. }
  319. public function getEncryptionAlgorithm($client_id = null)
  320. {
  321. if ($client_id) {
  322. $result = $this->collection('key_table')->findOne(array(
  323. 'client_id' => $client_id
  324. ));
  325. if ($result) {
  326. return $result['encryption_algorithm'];
  327. }
  328. }
  329. $result = $this->collection('key_table')->findOne(array(
  330. 'client_id' => null
  331. ));
  332. return is_null($result) ? 'RS256' : $result['encryption_algorithm'];
  333. }
  334. }