MongoDB.php 12 KB

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