Skip to content
This repository has been archived by the owner on Jul 10, 2023. It is now read-only.

Commit

Permalink
DEV Nextcloud: API Invite manager (#336)
Browse files Browse the repository at this point in the history
* add API routes, add ApiController

* add ApiController.php, add migration for ocm_tokens and ocm_remote_users table, add to migration to info.xml

* add API methods (authenticate, addToken, getToken, TokenList), change routes to GET for testing

* add API methods (authenticate, addToken, getToken, TokenList), change routes to GET for testing

* add new migration for db changes on remote_table & tokens, change API routes for initiator+get and post methods, Change ApiController for set request+ttoken validation+get header values+return proper response

* add comments

* some modification on apicontroller method for get the params instead of header, modification on one route from post to get, add some modification on database structure

* remove initiator from get_token method

* remove email param from ocm_remote_user query

* Adapt to new invite generation endpoint (draft)

* Fix token generation

* resolve conflicts on contact.js

* Adapt search plugin

* TODO: get permissions from inside the protocol

* fix token generator url json problem, #287

* add removed } for internal error

* Attempt to fix pondersource/sciencemesh-php#189

* Http::STATUS_INTERNAL_SERVER_ERROR

* Remove comment

* Deal with response from /sciencemesh/accept-invite

* Deal with non-URL idp

* add provider

* fix missing recipientHost in call to /sciencemesh/create-share

* params[share]... -> params...

* Fix storing of sharedSecret (token) in addSentShare

* working on #293

* Fixed reading permissions from the reva incoming request

* deal with null accepted users

* Mrv/share dialog (#297)

* fix incorrect remote in tne accept share dialog #294

Signed-off-by: MohammadReza vahedi <[email protected]>

* remove quotes from remote_id #295

Signed-off-by: MohammadReza vahedi <[email protected]>

* remove unnecessary log

---------

Signed-off-by: MohammadReza vahedi <[email protected]>

* get sharedSecret from the right array member

* Tag v0.2.0

* Tag v0.3.0

* Built v0.3.0

* info.xml did not validate: The value 'mit' is not an element of the set {'agpl', 'mpl', 'apache'}.

* Tag v0.3.1

* Built v0.3.1

* remove: error_logs

* remove: whitespace

* Fix #300

* Get remote out of URI

* Tag v0.4.0

* Build v0.4.0

* Update release.sh

* modify: initial use of inheritance for creating share provider class

* fix: call parent constructor to avoid breaking nextcloud

* modify: class signature to make parent constructor happy!

* fix: use statements.

* add: parent class stub file which has replaced private with protected.

* add: people who had contributed to the project and missing emails

* remove: unnecessary use statement

* modify: rename file and apply final changes

* fix: mixed space and tabs

* Add missed use statement

* remove error log

* add `use OCA\ScienceMesh\AppInfo\ScienceMeshApp;`

* fix config key

Signed-off-by: MohammadReza vahedi <[email protected]>

* add public api annotation

Signed-off-by: MohammadReza vahedi <[email protected]>

* bugfix and finalization

Signed-off-by: MohammadReza vahedi <[email protected]>

---------

Signed-off-by: MohammadReza vahedi <[email protected]>
Signed-off-by: MohammadReza vahedi <[email protected]>
Co-authored-by: Parham R <[email protected]>
Co-authored-by: Michiel de Jong <[email protected]>
Co-authored-by: root <[email protected]>
Co-authored-by: Yashar PourMohammad <[email protected]>
Co-authored-by: MohammadReza vahedi <[email protected]>
Co-authored-by: MohammadReza vahedi <[email protected]>
  • Loading branch information
7 people authored Jun 13, 2023
1 parent f0d2d8f commit 37c828f
Show file tree
Hide file tree
Showing 8 changed files with 554 additions and 2 deletions.
3 changes: 3 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<nextcloud min-version="15" max-version="26"/>
<owncloud min-version="10" max-version="10"/>
</dependencies>

<migration class="OCA\ScienceMesh\Migration\Version0001Date202305101530" app="sciencemesh" version="1.0.0" />

<settings>
<admin>OCA\ScienceMesh\Settings</admin>
<admin>OCA\ScienceMesh\Settings\SciencemeshSettingsAdmin</admin>
Expand Down
12 changes: 10 additions & 2 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,16 @@
["name" => "settings#get_settings", "url" => "/ajax/settings", "verb" => "GET"],
["name" => "settings#get_sciencemesh_settings", "url" => "/sciencemesh_settings", "verb" => "GET"],
["name" => "settings#save_sciencemesh_settings", "url" => "/ajax/sciencemesh_settings/save", "verb" => "GET"],
["name" => "settings#check_connection_settings", "url" => "/ajax/check_connection_settings", "verb" => "GET"]

["name" => "settings#check_connection_settings", "url" => "/ajax/check_connection_settings", "verb" => "GET"],

// API Routes
["name" => "api#add_token", "url" => "/api/v1/add_token/{initiator}", "verb" => "POST"],
["name" => "api#get_token", "url" => "/api/v1/get_token", "verb" => "GET"],
["name" => "api#tokens_list", "url" => "/api/v1/tokens_list/{initiator}", "verb" => "GET"],
["name" => "api#add_remote_user", "url" => "/api/v1/add_remote_user/{initiator}", "verb" => "POST"],
["name" => "api#get_remote_user", "url" => "/api/v1/get_remote_user/{initiator}", "verb" => "GET"],
["name" => "api#find_remote_user", "url" => "/api/v1/find_remote_user/{initiator}", "verb" => "GET"]

]
];

Expand Down
1 change: 1 addition & 0 deletions js/contacts.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
document.addEventListener("DOMContentLoaded", function(event) {
//Everything will be for working with contacts
loadData("");
var baseUrl = OC.generateUrl('/apps/sciencemesh');
$('#test_error').hide();
$.ajax({
Expand Down
333 changes: 333 additions & 0 deletions lib/Controller/ApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
<?php

namespace OCA\ScienceMesh\Controller;

use OCP\AppFramework\Http\JSONResponse;
use OCP\IRequest;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCA\ScienceMesh\AppConfig;
use OCA\ScienceMesh\Crypt;
use OCA\ScienceMesh\DocumentService;
use OCA\ScienceMesh\RevaHttpClient;
use OCP\AppFramework\Http\DataResponse;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\AppFramework\Http\TextPlainResponse;
use OCP\AppFramework\Http;
use OCA\Sciencemesh\ServerConfig;
use OCP\IConfig;

/**
* Settings controller for the administration page
*/
class ApiController extends Controller
{
private $logger;
private $config;
private $urlGenerator;
private $serverConfig;
private $sciencemeshConfig;
private $userId;
private $db;
const CATALOG_URL = "https://iop.sciencemesh.uni-muenster.de/iop/mentix/sitereg";

/**
* @param string $AppName - application name
* @param IRequest $request - request object
* @param IURLGenerator $urlGenerator - url generator service
* @param IL10N $trans - l10n service
* @param ILogger $logger - logger
* @param AppConfig $config - application configuration
*/
public function __construct(
$AppName,
IRequest $request,
IURLGenerator $urlGenerator,
IL10N $trans,
ILogger $logger,
AppConfig $config,
IConfig $sciencemeshConfig,
IDBConnection $db,
$userId
) {
parent::__construct($AppName, $request);
$this->serverConfig = new \OCA\ScienceMesh\ServerConfig($sciencemeshConfig);

$this->urlGenerator = $urlGenerator;
$this->logger = $logger;
$this->config = $config;
$this->sciencemeshConfig = $sciencemeshConfig;
$this->db = $db;
$this->request = $request;
}

/**
* Check if the request is authenticated by comparing the request's API key with the stored inviteManagerApikey.
*
*
* @PublicPage
* @param IRequest $request
* @return bool
*/
public function authentication($request)
{
$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('appconfig')
->where(
$qb->expr()->eq('appid', $qb->createNamedParameter('sciencemesh', IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('configkey', $qb->createNamedParameter('inviteManagerApikey', IQueryBuilder::PARAM_STR))
);

$cursor = $qb->execute();
$row = $cursor->fetchAll();

if ($row[0]['configvalue'] == $this->request->getHeader('apikey')) {
return true;
} else {
return false;
}
}

/**
*
* @PublicPage
* @NoCSRFRequired
*/
public function addToken($initiator, $request){
if(!$this->authentication($this->request)) return new DataResponse((['message' => 'Authentication failed!','status' => 412, 'data' => null]), Http::STATUS_INTERNAL_SERVER_ERROR);

if(!$this->request->getParam('token') and !$initiator and !$this->request->getParam('expiration') and !$this->request->getParam('description')){
return new DataResponse(['message' => 'values are not provided properly!','status' => 412, 'data' => null], Http::STATUS_OK);
}

$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('ocm_tokens')
->where(
$qb->expr()->eq('initiator', $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('token', $qb->createNamedParameter($this->request->getParam('token'), IQueryBuilder::PARAM_STR))
);
$cursor = $qb->execute();
$row = $cursor->fetchAll();

$expiration = $this->request->getParam('expiration');

if(empty($row)){
$qb->insert('ocm_tokens')
->values(
array(
'token' => $qb->createNamedParameter($this->request->getParam('token'), IQueryBuilder::PARAM_STR),
'initiator' => $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR),
'expiration' => $qb->createNamedParameter($expiration, IQueryBuilder::PARAM_STR),
'description' => $qb->createNamedParameter($this->request->getParam('description'), IQueryBuilder::PARAM_STR)
)
);
$cursor = $qb->execute();
}else{
$cursor = 0;
}

if($cursor)
return new DataResponse((['message' => 'Token added!','status' => 200, 'data' => $cursor]), Http::STATUS_OK);
else if($cursor == 0)
return new DataResponse((['message' => 'Token already exists!','status' => 204, 'data' => 0]), Http::STATUS_BAD_REQUEST);
else
return new DataResponse((['message' => 'Token added failed!','status' => 400, 'data' => 0]), Http::STATUS_BAD_REQUEST);
}

/**
* @PublicPage
* @NoCSRFRequired
*/
public function getToken($initiator){

if(!$this->authentication($this->request)) return new DataResponse((['message' => 'Authentication failed!','status' => 412, 'data' => null]), Http::STATUS_INTERNAL_SERVER_ERROR);

$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('ocm_tokens')
->where(
$qb->expr()->eq('token', $qb->createNamedParameter($this->request->getParam('token'), IQueryBuilder::PARAM_STR))
);

$cursor = $qb->execute();
$row = $cursor->fetchAll();

if(empty($row)){
return new DataResponse((['message' => 'No Token found!','status' => 201, 'data' => '']), Http::STATUS_BAD_REQUEST);
}else{
return new DataResponse(($row[0]), Http::STATUS_OK);
}

}


/**
* @PublicPage
* @NoCSRFRequired
*/
public function tokensList($initiator){

if(!$this->authentication($this->request)) return new DataResponse((['message' => 'Authentication failed!','status' => 412, 'data' => null]), Http::STATUS_INTERNAL_SERVER_ERROR);

$qb = $this->db->getQueryBuilder();

$qb->select('*')
->where(
$qb->expr()->eq('initiator', $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR))
)
->from('ocm_tokens');

$cursor = $qb->execute();
$row = $cursor->fetchAll();
return new DataResponse(($row), Http::STATUS_OK);
}


/**
* @PublicPage
* @NoCSRFRequired
*/
public function addRemoteUser($initiator){

if(!$this->authentication($this->request)) return new DataResponse((['message' => 'Authentication failed!','status' => 412, 'data' => null]), Http::STATUS_INTERNAL_SERVER_ERROR);

if(!$this->request->getParam('opaqueUserId') and !$this->request->getParam('idp') and !$this->request->getParam('email') and !$this->request->getParam('displayName')){
return new DataResponse((['message' => 'values are not provided properly!','status' => 412, 'data' => null]), Http::STATUS_OK);
}

$qb = $this->db->getQueryBuilder();


$qb->select('*')
->from('ocm_remote_users')
->where(
$qb->expr()->eq('opaque_user_id', $qb->createNamedParameter($this->request->getParam('opaqueUserId'), IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('idp', $qb->createNamedParameter($this->request->getParam('idp'), IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('email', $qb->createNamedParameter($this->request->getParam('email'), IQueryBuilder::PARAM_STR))
);
$cursor = $qb->execute();
$row = $cursor->fetchAll();

if(empty($row)){
$qb->insert('ocm_remote_users')
->values(
array(
'initiator' => $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR),
'opaque_user_id' => $qb->createNamedParameter($this->request->getParam('opaqueUserId'), IQueryBuilder::PARAM_STR),
'idp' => $qb->createNamedParameter($this->request->getParam('idp'), IQueryBuilder::PARAM_STR),
'email' => $qb->createNamedParameter($this->request->getParam('email'), IQueryBuilder::PARAM_STR),
'display_name' => $qb->createNamedParameter($this->request->getParam('displayName'), IQueryBuilder::PARAM_STR)
)
);
$cursor = $qb->execute();
}else{
$cursor = 0;
}

if($cursor || !empty($row))
if(!empty($row))
return new DataResponse((['message' => 'User exists!','status' => 400, 'data' => $row]), Http::STATUS_BAD_REQUEST);
if($cursor)
return new DataResponse((['message' => 'User added!','status' => 200, 'data' => $cursor]), Http::STATUS_OK);
else
return new DataResponse((['message' => 'User does not added!','status' => 500, 'data' => 0]), Http::STATUS_INTERNAL_SERVER_ERROR);
}


/**
* @PublicPage
* @NoCSRFRequired
*/
public function getRemoteUser($initiator){

if(!$this->authentication($this->request)) return new DataResponse((['message' => 'Authentication failed!','status' => 412, 'data' => null]), Http::STATUS_INTERNAL_SERVER_ERROR);

$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('ocm_remote_users')
->where(
$qb->expr()->eq('initiator', $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('idp', $qb->createNamedParameter($this->request->getParam('idp'), IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->eq('opaque_user_id', $qb->createNamedParameter($this->request->getParam('opaqueUserId'), IQueryBuilder::PARAM_STR))
);

$cursor = $qb->execute();
$row = $cursor->fetchAll();

if(empty($row)){
return new DataResponse((['message' => 'User not found!','status' => 201, 'data' => '']), Http::STATUS_BAD_REQUEST);
}else{
return new DataResponse(($this->CastToRevaUser($row[0])), Http::STATUS_OK);
}

}


/**
* @PublicPage
* @NoCSRFRequired
*/
public function findRemoteUser($initiator){

$qb = $this->db->getQueryBuilder();

$qb->select('*')
->from('ocm_remote_users')
->where(
$qb->expr()->eq('initiator', $qb->createNamedParameter($initiator, IQueryBuilder::PARAM_STR))
)
->andWhere(
$qb->expr()->orX(
$qb->expr()->like('opaque_user_id', $qb->createNamedParameter($this->request->getParam('search'), IQueryBuilder::PARAM_STR)),
$qb->expr()->like('idp', $qb->createNamedParameter($this->request->getParam('search'), IQueryBuilder::PARAM_STR)),
$qb->expr()->like('email', $qb->createNamedParameter($this->request->getParam('search'), IQueryBuilder::PARAM_STR)),
$qb->expr()->like('display_name', $qb->createNamedParameter($this->request->getParam('search'), IQueryBuilder::PARAM_STR))
)
);

$cursor = $qb->execute();
$row = $cursor->fetchAll();

if(empty($row)){
return new DataResponse(([ ]), Http::STATUS_OK);
}else{
$result = [];
foreach($row as $item){
$result[] = $this->CastToRevaUser($item);
}
return new DataResponse(($result), Http::STATUS_OK);
}

}

private function CastToRevaUser($user){
return array(
'opaqueUserId' => $user['opaque_user_id'],
'idp' => $user['idp'],
'email' => $user['email'],
'displayName' => $user['display_name']
);
}
}
3 changes: 3 additions & 0 deletions lib/Controller/AppController.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ public function invitationsGenerate() {
$recipient = $this->request->getParam('email');
$invitationsData = $this->httpClient->generateTokenFromReva($this->userId, $recipient);
$inviteLinkStr = $invitationsData["invite_link"];
$iopUrl = $invitationsData["user_id"]["idp"];
$iopDomain = parse_url($iopUrl)["host"];
// $meshDirectoryUrl = "https://sciencemesh.cesnet.cz/iop/meshdir/";
$meshDirectoryUrl = $this->config->getAppValue('sciencemesh', 'meshDirectoryUrl', 'https://sciencemesh.cesnet.cz/iop/meshdir/');
if (!$inviteLinkStr) {
return new TextPlainResponse("Unexpected response from Reva", Http::STATUS_INTERNAL_SERVER_ERROR);
Expand Down
Loading

0 comments on commit 37c828f

Please sign in to comment.