Get the access control adapter
public getAccessControl ( ) : Imbo\Auth\AccessControl\Adapter\AdapterInterface | ||
리턴 | Imbo\Auth\AccessControl\Adapter\AdapterInterface |
/** * {@inheritdoc} */ public function checkAccessToken(EventInterface $event) { $request = $event->getRequest(); $response = $event->getResponse(); $query = $request->query; $eventName = $event->getName(); $config = $event->getConfig(); if (($eventName === 'image.get' || $eventName === 'image.head') && $this->isWhitelisted($request)) { // All transformations in the request are whitelisted. Skip the access token check return; } // If the response has a short URL header, we can skip the access token check if ($response->headers->has('X-Imbo-ShortUrl')) { return; } if (!$query->has('accessToken')) { throw new RuntimeException('Missing access token', 400); } $token = $query->get('accessToken'); // First the the raw un-encoded URI, then the URI as is $uris = [$request->getRawUri(), $request->getUriAsIs()]; $privateKey = $event->getAccessControl()->getPrivateKey($request->getPublicKey()); // append uris with [] expanded or [0] reduced $uris[] = $this->getUnescapedAlternativeURL($request->getRawUri()); $uris[] = $this->getEscapedAlternativeURL($request->getRawUri()); // See if we should modify the protocol for the incoming request $protocol = $config['authentication']['protocol']; if ($protocol === 'both') { $uris = array_reduce($uris, function ($dest, $uri) { $baseUrl = preg_replace('#^https?#', '', $uri); $dest[] = 'http' . $baseUrl; $dest[] = 'https' . $baseUrl; return $dest; }, []); } else { if (in_array($protocol, ['http', 'https'])) { $uris = array_map(function ($uri) use($protocol) { return preg_replace('#^https?#', $protocol, $uri); }, $uris); } } foreach ($uris as $uri) { // Remove the access token from the query string as it's not used to generate the HMAC $uri = rtrim(preg_replace('/(?<=(\\?|&))accessToken=[^&]+&?/', '', $uri), '&?'); $correctToken = hash_hmac('sha256', $uri, $privateKey); if ($correctToken === $token) { return; } } throw new RuntimeException('Incorrect access token', 400); }
/** * Delete public key * * @param EventInterface $event The current event */ public function deleteKey(EventInterface $event) { $acl = $event->getAccessControl(); if (!$acl instanceof MutableAdapterInterface) { throw new ResourceException('Access control adapter is immutable', 405); } $request = $event->getRequest(); $publicKey = $request->getRoute()->get('publickey'); $keyExists = $acl->publicKeyExists($publicKey); if (!$keyExists) { throw new RuntimeException('Public key not found', 404); } $request = $event->getRequest(); $publicKey = $request->getRoute()->get('publickey'); $acl->deletePublicKey($publicKey); }
/** * Handle GET and HEAD requests * * @param EventInterface $event The current event */ public function getImages(EventInterface $event) { $acl = $event->getAccessControl(); $missingAccess = []; $users = $event->getRequest()->getUsers(); foreach ($users as $user) { $hasAccess = $acl->hasAccess($event->getRequest()->getPublicKey(), 'images.get', $user); if (!$hasAccess) { $missingAccess[] = $user; } } if (!empty($missingAccess)) { throw new RuntimeException('Public key does not have access to the users: [' . implode(', ', $missingAccess) . ']', 400); } $event->getManager()->trigger('db.images.load', ['users' => $users]); }
/** * Delete a resource group * * @param EventInterface $event The current event */ public function deleteGroup(EventInterface $event) { $accessControl = $event->getAccessControl(); if (!$accessControl instanceof MutableAdapterInterface) { throw new ResourceException('Access control adapter is immutable', 405); } $route = $event->getRequest()->getRoute(); $groupName = $route->get('group'); $group = $accessControl->getGroup($groupName); if (!$group) { throw new ResourceException('Resource group not found', 404); } $accessControl->deleteResourceGroup($groupName); }
/** * Check that the public key has access to the users * * @param Imbo\EventListener\ListenerInterface $event * @param array $users Array of user strings */ protected function validateAccess(EventInterface $event, array $users) { $acl = $event->getAccessControl(); $missingAccess = []; foreach ($users as $user) { $hasAccess = $acl->hasAccess($event->getRequest()->getPublicKey(), 'images.get', $user); if (!$hasAccess) { $missingAccess[] = $user; } } if (!empty($missingAccess)) { throw new RuntimeException('Public key does not have access to the users: [' . implode(', ', $missingAccess) . ']', 400); } }
/** * Validate the contents of an access rule. * * @param array $rule Access rule to check * @throws RuntimeException */ private function validateRule(EventInterface $event, array $rule) { $acl = $event->getAccessControl(); $allowedProperties = ['resources', 'group', 'users']; $unknownProperties = array_diff(array_keys($rule), $allowedProperties); if (!empty($unknownProperties)) { throw new RuntimeException('Found unknown properties in rule: [' . implode(', ', $unknownProperties) . ']', 400); } if (isset($rule['resources']) && isset($rule['group'])) { throw new RuntimeException('Both resources and group found in rule', 400); } if (!isset($rule['resources']) && !isset($rule['group'])) { throw new RuntimeException('Neither group nor resources found in rule', 400); } if (isset($rule['resources']) && !$this->isStringArray($rule['resources'])) { throw new RuntimeException('Illegal value in resources array. String array expected', 400); } if (isset($rule['group'])) { if (!is_string($rule['group'])) { throw new RuntimeException('Group must be specified as a string value', 400); } if (!$acl->getGroup($rule['group'])) { throw new RuntimeException('Group \'' . $rule['group'] . '\' does not exist', 400); } } if (!isset($rule['users'])) { throw new RuntimeException('Users not specified in rule', 400); } if ($rule['users'] !== '*' && !$this->isStringArray($rule['users'])) { throw new RuntimeException('Illegal value for users property. Allowed: \'*\' or array with users', 400); } }
/** * {@inheritdoc} */ public function authenticate(EventInterface $event) { $response = $event->getResponse(); $request = $event->getRequest(); $config = $event->getConfig(); // Whether or not the authentication info is in the request headers $fromHeaders = $request->headers->has('x-imbo-authenticate-timestamp') && $request->headers->has('x-imbo-authenticate-signature'); // Fetch timestamp header, fallback to query param $timestamp = $request->headers->get('x-imbo-authenticate-timestamp', $request->query->get('timestamp')); if (!$timestamp) { $exception = new RuntimeException('Missing authentication timestamp', 400); $exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM); } else { if (!$this->timestampIsValid($timestamp)) { $exception = new RuntimeException('Invalid timestamp: ' . $timestamp, 400); $exception->setImboErrorCode(Exception::AUTH_INVALID_TIMESTAMP); } else { if ($this->timestampHasExpired($timestamp)) { $exception = new RuntimeException('Timestamp has expired: ' . $timestamp, 400); $exception->setImboErrorCode(Exception::AUTH_TIMESTAMP_EXPIRED); } } } if (isset($exception)) { throw $exception; } // Fetch signature header, fallback to query param $signature = $request->headers->get('x-imbo-authenticate-signature', $request->query->get('signature')); if (!$signature) { $exception = new RuntimeException('Missing authentication signature', 400); $exception->setImboErrorCode(Exception::AUTH_MISSING_PARAM); } if (isset($exception)) { throw $exception; } $publicKey = $request->getPublicKey(); $privateKey = $event->getAccessControl()->getPrivateKey($publicKey); $url = $request->getRawUri(); if (!$fromHeaders) { // Remove the signature and timestamp from the query parameters as they are not used // when generating the HMAC $url = rtrim(preg_replace('/(?<=(\\?|&))(signature|timestamp)=[^&]+&?/', '', $url), '&?'); } // See if we should modify the protocol for the incoming request $uris = [$url]; $protocol = $config['authentication']['protocol']; if ($protocol === 'both') { $uris = [preg_replace('#^https?#', 'http', $url), preg_replace('#^https?#', 'https', $url)]; } else { if (in_array($protocol, ['http', 'https'])) { $uris = [preg_replace('#^https?#', $protocol, $url)]; } } // Add the URL used for auth to the response headers $response->headers->set('X-Imbo-AuthUrl', implode(', ', $uris)); foreach ($uris as $uri) { if ($this->signatureIsValid($request->getMethod(), $uri, $publicKey, $privateKey, $timestamp, $signature)) { return; } } $exception = new RuntimeException('Signature mismatch', 400); $exception->setImboErrorCode(Exception::AUTH_SIGNATURE_MISMATCH); throw $exception; }
/** * Load groups from the configured access control adapter * * @param EventInterface $event An event instance */ public function loadGroups(EventInterface $event) { $query = new GroupQuery(); $params = $event->getRequest()->query; if ($params->has('page')) { $query->page($params->get('page')); } if ($params->has('limit')) { $query->limit($params->get('limit')); } $response = $event->getResponse(); $aclAdapter = $event->getAccessControl(); // Create the model and set some pagination values $model = new GroupsModel(); $model->setLimit($query->limit())->setPage($query->page()); $groups = $aclAdapter->getGroups($query, $model); $modelGroups = []; foreach ($groups as $groupName => $resources) { $modelGroups[] = ['name' => $groupName, 'resources' => $resources]; } $model->setGroups($modelGroups); $response->setModel($model); }