Receive the proxy server from the app
This allows users to use one client against multiple different servers Signed-off-by: Joas Schilling <coding@schilljs.com>
This commit is contained in:
Родитель
3e53f38630
Коммит
62605403a4
|
@ -160,6 +160,12 @@
|
|||
<notnull>true</notnull>
|
||||
<length>128</length>
|
||||
</field>
|
||||
<field>
|
||||
<name>proxyserver</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
<length>256</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>oc_notifpushtoken</name>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
In order to find out if notifications support push on the server you can run a request against the capabilities endpoint: `/ocs/v2.php/cloud/capabilities`
|
||||
|
||||
```json
|
||||
```
|
||||
{
|
||||
"ocs": {
|
||||
...
|
||||
|
@ -36,14 +36,15 @@ In order to find out if notifications support push on the server you can run a r
|
|||
|
||||
3. The device generates a `sha512` hash of the `PushToken` (`PushTokenHash`)
|
||||
|
||||
4. The device then sends the `devicePublicKey` and `PushTokenHash` to the Nextcloud server:
|
||||
4. The device then sends the `devicePublicKey`, `PushTokenHash` and `proxyServerUrl` to the Nextcloud server:
|
||||
|
||||
```json
|
||||
```
|
||||
POST /ocs/v2.php/apps/notifications/api/v3/push
|
||||
|
||||
{
|
||||
"pushTokenHash": "{{PushTokenHash}}",
|
||||
"devicePublicKey": "{{devicePublicKey}}"
|
||||
"devicePublicKey": "{{devicePublicKey}}",
|
||||
"proxyServer": "{{proxyServerUrl}}"
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -83,6 +84,7 @@ In case of `400` the following `message` can appear in the body:
|
|||
| `INVALID_PUSHTOKEN_HASH` | The hash of the push token was not a valid `sha512` hash. |
|
||||
| `INVALID_SESSION_TOKEN` | The authentication token of the request could not be identified. Check whether a password was used to login. |
|
||||
| `INVALID_DEVICE_KEY` | The device key does not match the one registered to the provided session token. |
|
||||
| `INVALID_PROXY_SERVER` | The proxy server was not a valid https URL. |
|
||||
|
||||
|
||||
|
||||
|
@ -92,14 +94,10 @@ When an account is removed from a device, the device should unregister on the se
|
|||
|
||||
|
||||
|
||||
The device should then send the `devicePublicKey` and `PushTokenHash` to the Nextcloud server:
|
||||
The device should then send a `DELETE` request to the Nextcloud server:
|
||||
|
||||
```json
|
||||
```
|
||||
DELETE /ocs/v2.php/apps/notifications/api/v3/push
|
||||
|
||||
{
|
||||
"devicePublicKey": "{{devicePublicKey}}"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
@ -110,6 +108,7 @@ The server replies with the following status codes:
|
|||
|
||||
| Status code | Meaning |
|
||||
| ----------- | ---------------------------------------- |
|
||||
| 200 | Push token was not registered on the server |
|
||||
| 202 | Push token was deleted and **needs to be deleted from the `Proxy`** |
|
||||
| 400 | Invalid public key or device does not use a token to authenticate |
|
||||
| 401 | Device is not logged in |
|
||||
|
@ -131,7 +130,7 @@ In case of `400` the following `message` can appear in the body:
|
|||
|
||||
The device sends the`PushToken` as well as the `deviceIdentifier`, `signature` and the user´s `publicKey` (from the server´s response) to the Push Proxy:
|
||||
|
||||
```json
|
||||
```
|
||||
POST /devices
|
||||
|
||||
{
|
||||
|
@ -161,7 +160,7 @@ The server replies with the following status codes:
|
|||
|
||||
The device sends the `deviceIdentifier`, `deviceIdentifierSignature` and the user´s `publicKey` (from the server´s response) to the Push Proxy:
|
||||
|
||||
```json
|
||||
```
|
||||
DELETE /devices
|
||||
|
||||
{
|
||||
|
@ -190,7 +189,9 @@ The server replies with the following status codes:
|
|||
The pushed notifications format depends on the service that is used.
|
||||
In case of Apple Push Notification Service, the payload of a push notification looks like the following:
|
||||
|
||||
```json
|
||||
***⚠️ Warning:** Maybe different since we switched to FCM instead of APNS*
|
||||
|
||||
```
|
||||
{
|
||||
"aps" : {
|
||||
"alert" : {
|
||||
|
|
|
@ -77,9 +77,10 @@ class PushController extends OCSController {
|
|||
*
|
||||
* @param string $pushTokenHash
|
||||
* @param string $devicePublicKey
|
||||
* @param string $proxyServer
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function registerDevice($pushTokenHash, $devicePublicKey) {
|
||||
public function registerDevice($pushTokenHash, $devicePublicKey, $proxyServer) {
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user instanceof IUser) {
|
||||
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
|
||||
|
@ -95,6 +96,10 @@ class PushController extends OCSController {
|
|||
return new JSONResponse(['message' => 'INVALID_DEVICE_KEY'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (!filter_input(FILTER_VALIDATE_URL, $proxyServer) || strpos($proxyServer, 'https://') !== 0 || strlen($proxyServer)> 256) {
|
||||
return new JSONResponse(['message' => 'INVALID_PROXY_SERVER'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$tokenId = $this->session->get('token-id');
|
||||
try {
|
||||
$token = $this->tokenProvider->getTokenById($tokenId);
|
||||
|
@ -108,11 +113,7 @@ class PushController extends OCSController {
|
|||
openssl_sign($deviceIdentifier, $signature, $key->getPrivate(), OPENSSL_ALGO_SHA512);
|
||||
$deviceIdentifier = base64_encode(hash('sha512', $deviceIdentifier, true));
|
||||
|
||||
try {
|
||||
$created = $this->savePushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash);
|
||||
} catch (\BadMethodCallException $e) {
|
||||
return new JSONResponse(['message' => 'INVALID_DEVICE_KEY'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
$created = $this->savePushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer);
|
||||
|
||||
return new JSONResponse([
|
||||
'publicKey' => $key->getPublic(),
|
||||
|
@ -125,21 +126,14 @@ class PushController extends OCSController {
|
|||
* @NoAdminRequired
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $devicePublicKey
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function removeDevice($devicePublicKey) {
|
||||
public function removeDevice() {
|
||||
$user = $this->userSession->getUser();
|
||||
if (!$user instanceof IUser) {
|
||||
return new JSONResponse([], Http::STATUS_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (strlen($devicePublicKey) !== 450 ||
|
||||
strpos($devicePublicKey, '-----BEGIN PUBLIC KEY-----') !== 0 ||
|
||||
strpos($devicePublicKey, '-----END PUBLIC KEY-----') !== 426) {
|
||||
return new JSONResponse(['message' => 'INVALID_DEVICE_KEY'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
$sessionId = $this->session->getId();
|
||||
try {
|
||||
$token = $this->tokenProvider->getToken($sessionId);
|
||||
|
@ -147,13 +141,11 @@ class PushController extends OCSController {
|
|||
return new JSONResponse(['message' => 'INVALID_SESSION_TOKEN'], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->deletePushToken($user, $token, $devicePublicKey);
|
||||
} catch (\BadMethodCallException $e) {
|
||||
return new JSONResponse(['message' => 'INVALID_DEVICE_KEY'], Http::STATUS_BAD_REQUEST);
|
||||
if ($this->deletePushToken($user, $token)) {
|
||||
return new JSONResponse([], Http::STATUS_ACCEPTED);
|
||||
}
|
||||
|
||||
return new JSONResponse([], Http::STATUS_ACCEPTED);
|
||||
return new JSONResponse([], Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,26 +154,24 @@ class PushController extends OCSController {
|
|||
* @param string $deviceIdentifier
|
||||
* @param string $devicePublicKey
|
||||
* @param string $pushTokenHash
|
||||
* @param string $proxyServer
|
||||
* @return bool If the hash was new to the database
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
protected function savePushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash) {
|
||||
protected function savePushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer) {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('pushtokenhash')
|
||||
$query->select('*')
|
||||
->from('notifications_pushtokens')
|
||||
->where($query->expr()->eq('uid', $query->createNamedParameter($user->getUID())))
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId())))
|
||||
->andWhere($query->expr()->eq('devicepublickey', $query->createNamedParameter($devicePublicKey)));
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId())));
|
||||
$result = $query->execute();
|
||||
$row = $result->fetch();
|
||||
$result->closeCursor();
|
||||
|
||||
if (!$row) {
|
||||
return $this->insertPushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash);
|
||||
} else if ($row['pushtokenhash'] !== $pushTokenHash) {
|
||||
return $this->updatePushToken($user, $token, $devicePublicKey, $pushTokenHash);
|
||||
return $this->insertPushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer);
|
||||
}
|
||||
return false;
|
||||
|
||||
return $this->updatePushToken($user, $token, $devicePublicKey, $pushTokenHash, $proxyServer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -190,9 +180,10 @@ class PushController extends OCSController {
|
|||
* @param string $deviceIdentifier
|
||||
* @param string $devicePublicKey
|
||||
* @param string $pushTokenHash
|
||||
* @param string $proxyServer
|
||||
* @return bool If the entry was created
|
||||
*/
|
||||
protected function insertPushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash) {
|
||||
protected function insertPushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer) {
|
||||
$devicePublicKeyHash = hash('sha512', $devicePublicKey);
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
@ -204,6 +195,7 @@ class PushController extends OCSController {
|
|||
'devicepublickey' => $query->createNamedParameter($devicePublicKey),
|
||||
'devicepublickeyhash' => $query->createNamedParameter($devicePublicKeyHash),
|
||||
'pushtokenhash' => $query->createNamedParameter($pushTokenHash),
|
||||
'proxyserver' => $query->createNamedParameter($proxyServer),
|
||||
]);
|
||||
return $query->execute() > 0;
|
||||
}
|
||||
|
@ -213,46 +205,35 @@ class PushController extends OCSController {
|
|||
* @param IToken $token
|
||||
* @param string $devicePublicKey
|
||||
* @param string $pushTokenHash
|
||||
* @param string $proxyServer
|
||||
* @return bool If the entry was updated
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
protected function updatePushToken(IUser $user, IToken $token, $devicePublicKey, $pushTokenHash) {
|
||||
protected function updatePushToken(IUser $user, IToken $token, $devicePublicKey, $pushTokenHash, $proxyServer) {
|
||||
$devicePublicKeyHash = hash('sha512', $devicePublicKey);
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->update('notifications_pushtokens')
|
||||
->set('devicepublickey', $query->createNamedParameter($devicePublicKey))
|
||||
->set('devicepublickeyhash', $query->createNamedParameter($devicePublicKeyHash))
|
||||
->set('pushtokenhash', $query->createNamedParameter($pushTokenHash))
|
||||
->set('proxyserver', $query->createNamedParameter($proxyServer))
|
||||
->where($query->expr()->eq('uid', $query->createNamedParameter($user->getUID())))
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId(), IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('devicepublickeyhash', $query->createNamedParameter($devicePublicKeyHash)));
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId(), IQueryBuilder::PARAM_INT)));
|
||||
|
||||
if ($query->execute() !== 0) {
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
return true;
|
||||
return $query->execute() !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
* @param IToken $token
|
||||
* @param string $devicePublicKey
|
||||
* @return bool If the entry was deleted
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
protected function deletePushToken(IUser $user, IToken $token, $devicePublicKey) {
|
||||
$devicePublicKeyHash = hash('sha512', $devicePublicKey);
|
||||
|
||||
protected function deletePushToken(IUser $user, IToken $token) {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete('notifications_pushtokens')
|
||||
->where($query->expr()->eq('uid', $query->createNamedParameter($user->getUID())))
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId(), IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($query->expr()->eq('devicepublickeyhash', $query->createNamedParameter($devicePublicKeyHash)));
|
||||
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId(), IQueryBuilder::PARAM_INT)));
|
||||
|
||||
if ($query->execute() !== 0) {
|
||||
throw new \BadMethodCallException();
|
||||
}
|
||||
|
||||
return true;
|
||||
return $query->execute() !== 0;
|
||||
}
|
||||
}
|
||||
|
|
60
lib/Push.php
60
lib/Push.php
|
@ -88,7 +88,10 @@ class Push {
|
|||
$pushNotifications = [];
|
||||
foreach ($devices as $device) {
|
||||
try {
|
||||
$pushNotifications[] = json_encode($this->encryptAndSign($userKey, $device, $notification));
|
||||
if (!isset($pushNotifications[$device['proxyserver']])) {
|
||||
$pushNotifications[$device['proxyserver']] = [];
|
||||
}
|
||||
$pushNotifications[$device['proxyserver']] = json_encode($this->encryptAndSign($userKey, $device, $notification));
|
||||
} catch (InvalidTokenException $e) {
|
||||
// Token does not exist anymore, should drop the push device entry
|
||||
// FIXME delete push token
|
||||
|
@ -99,33 +102,36 @@ class Push {
|
|||
}
|
||||
|
||||
$client = $this->clientService->newClient();
|
||||
try {
|
||||
$pushServer = rtrim($this->config->getAppValue('notifications', 'push_server', 'https://push-notifications.nextcloud.com'), '/');
|
||||
$response = $client->post($pushServer . '/notifications', [
|
||||
'body' => [
|
||||
'notifications' => $pushNotifications,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->log->logException($e, [
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
return;
|
||||
}
|
||||
foreach ($pushNotifications as $proxyServer => $notifications) {
|
||||
try {
|
||||
$response = $client->post(rtrim($proxyServer, '/') . '/notifications', [
|
||||
'body' => [
|
||||
'notifications' => $notifications,
|
||||
],
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$this->log->logException($e, [
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
$status = $response->getStatusCode();
|
||||
if ($status !== Http::STATUS_OK && $status !== Http::STATUS_SERVICE_UNAVAILABLE) {
|
||||
$body = $response->getBody();
|
||||
$this->log->error('Could not send notification to push server: {error}',[
|
||||
'error' => is_string($body) ? $body : 'no reason given',
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
} else if ($status === Http::STATUS_SERVICE_UNAVAILABLE && $this->config->getSystemValue('debug', false)) {
|
||||
$body = $response->getBody();
|
||||
$this->log->debug('Could not send notification to push server: {error}',[
|
||||
'error' => is_string($body) ? $body : 'no reason given',
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
$status = $response->getStatusCode();
|
||||
if ($status !== Http::STATUS_OK && $status !== Http::STATUS_SERVICE_UNAVAILABLE) {
|
||||
$body = $response->getBody();
|
||||
$this->log->error('Could not send notification to push server [{url}]: {error}',[
|
||||
'error' => is_string($body) ? $body : 'no reason given',
|
||||
'url' => $proxyServer,
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
} else if ($status === Http::STATUS_SERVICE_UNAVAILABLE && $this->config->getSystemValue('debug', false)) {
|
||||
$body = $response->getBody();
|
||||
$this->log->debug('Could not send notification to push server [{url}]: {error}',[
|
||||
'error' => is_string($body) ? $body : 'no reason given',
|
||||
'url' => $proxyServer,
|
||||
'app' => 'notifications',
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче