Merge pull request #9830 from nextcloud/feature/noid/oauth_vue_redirect_validate
Migrate OAuth Admin settings to vue
This commit is contained in:
commit
c3aea9cdf6
|
@ -0,0 +1,25 @@
|
||||||
|
all: dev-setup build-js-production
|
||||||
|
|
||||||
|
dev-setup: clean clean-dev npm-init
|
||||||
|
|
||||||
|
npm-init:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
npm-update:
|
||||||
|
npm update
|
||||||
|
|
||||||
|
build-js:
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
build-js-production:
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
watch-js:
|
||||||
|
npm run watch
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f js/oauth2.js
|
||||||
|
rm -f js/oauth2.map
|
||||||
|
|
||||||
|
clean-dev:
|
||||||
|
rm -rf node_modules
|
|
@ -23,13 +23,18 @@ return [
|
||||||
'routes' => [
|
'routes' => [
|
||||||
[
|
[
|
||||||
'name' => 'Settings#addClient',
|
'name' => 'Settings#addClient',
|
||||||
'url' => '/settings',
|
'url' => '/clients',
|
||||||
'verb' => 'POST',
|
'verb' => 'POST',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'name' => 'Settings#getClients',
|
||||||
|
'url' => '/clients',
|
||||||
|
'verb' => 'GET',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'name' => 'Settings#deleteClient',
|
'name' => 'Settings#deleteClient',
|
||||||
'url' => '/clients/{id}/delete',
|
'url' => '/clients/{id}',
|
||||||
'verb' => 'POST'
|
'verb' => 'DELETE'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'LoginRedirector#authorize',
|
'name' => 'LoginRedirector#authorize',
|
||||||
|
|
|
@ -3,3 +3,18 @@
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#oauth2 .icon-toggle,
|
||||||
|
#oauth2 .icon-delete {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
padding: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#oauth2 .grid td code {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -1,15 +0,0 @@
|
||||||
$(document).ready(function () {
|
|
||||||
|
|
||||||
$('.show-oauth-credentials').click(function() {
|
|
||||||
var row = $(this).parent();
|
|
||||||
var code = $(row).find('code');
|
|
||||||
if(code.text() === '****') {
|
|
||||||
code.text(row.data('value'));
|
|
||||||
$(this).css('opacity', 0.9);
|
|
||||||
} else {
|
|
||||||
code.text('****');
|
|
||||||
$(this).css('opacity', 0.3);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
|
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
|
||||||
*
|
*
|
||||||
|
@ -26,14 +27,11 @@ use OCA\OAuth2\Db\AccessTokenMapper;
|
||||||
use OCA\OAuth2\Db\Client;
|
use OCA\OAuth2\Db\Client;
|
||||||
use OCA\OAuth2\Db\ClientMapper;
|
use OCA\OAuth2\Db\ClientMapper;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
use OCP\AppFramework\Http\RedirectResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IURLGenerator;
|
|
||||||
use OCP\Security\ISecureRandom;
|
use OCP\Security\ISecureRandom;
|
||||||
|
|
||||||
class SettingsController extends Controller {
|
class SettingsController extends Controller {
|
||||||
/** @var IURLGenerator */
|
|
||||||
private $urlGenerator;
|
|
||||||
/** @var ClientMapper */
|
/** @var ClientMapper */
|
||||||
private $clientMapper;
|
private $clientMapper;
|
||||||
/** @var ISecureRandom */
|
/** @var ISecureRandom */
|
||||||
|
@ -48,53 +46,68 @@ class SettingsController extends Controller {
|
||||||
/**
|
/**
|
||||||
* @param string $appName
|
* @param string $appName
|
||||||
* @param IRequest $request
|
* @param IRequest $request
|
||||||
* @param IURLGenerator $urlGenerator
|
|
||||||
* @param ClientMapper $clientMapper
|
* @param ClientMapper $clientMapper
|
||||||
* @param ISecureRandom $secureRandom
|
* @param ISecureRandom $secureRandom
|
||||||
* @param AccessTokenMapper $accessTokenMapper
|
* @param AccessTokenMapper $accessTokenMapper
|
||||||
* @param DefaultTokenMapper $defaultTokenMapper
|
* @param DefaultTokenMapper $defaultTokenMapper
|
||||||
*/
|
*/
|
||||||
public function __construct($appName,
|
public function __construct(string $appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
IURLGenerator $urlGenerator,
|
|
||||||
ClientMapper $clientMapper,
|
ClientMapper $clientMapper,
|
||||||
ISecureRandom $secureRandom,
|
ISecureRandom $secureRandom,
|
||||||
AccessTokenMapper $accessTokenMapper,
|
AccessTokenMapper $accessTokenMapper,
|
||||||
DefaultTokenMapper $defaultTokenMapper
|
DefaultTokenMapper $defaultTokenMapper
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
$this->urlGenerator = $urlGenerator;
|
|
||||||
$this->secureRandom = $secureRandom;
|
$this->secureRandom = $secureRandom;
|
||||||
$this->clientMapper = $clientMapper;
|
$this->clientMapper = $clientMapper;
|
||||||
$this->accessTokenMapper = $accessTokenMapper;
|
$this->accessTokenMapper = $accessTokenMapper;
|
||||||
$this->defaultTokenMapper = $defaultTokenMapper;
|
$this->defaultTokenMapper = $defaultTokenMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function addClient(string $name,
|
||||||
* @param string $name
|
string $redirectUri): JSONResponse {
|
||||||
* @param string $redirectUri
|
|
||||||
* @return RedirectResponse
|
|
||||||
*/
|
|
||||||
public function addClient($name,
|
|
||||||
$redirectUri) {
|
|
||||||
$client = new Client();
|
$client = new Client();
|
||||||
$client->setName($name);
|
$client->setName($name);
|
||||||
$client->setRedirectUri($redirectUri);
|
$client->setRedirectUri($redirectUri);
|
||||||
$client->setSecret($this->secureRandom->generate(64, self::validChars));
|
$client->setSecret($this->secureRandom->generate(64, self::validChars));
|
||||||
$client->setClientIdentifier($this->secureRandom->generate(64, self::validChars));
|
$client->setClientIdentifier($this->secureRandom->generate(64, self::validChars));
|
||||||
$this->clientMapper->insert($client);
|
$client = $this->clientMapper->insert($client);
|
||||||
return new RedirectResponse($this->urlGenerator->getAbsoluteURL('/index.php/settings/admin/security'));
|
|
||||||
|
$result = [
|
||||||
|
'id' => $client->getId(),
|
||||||
|
'name' => $client->getName(),
|
||||||
|
'redirectUri' => $client->getRedirectUri(),
|
||||||
|
'clientId' => $client->getClientIdentifier(),
|
||||||
|
'clientSecret' => $client->getSecret(),
|
||||||
|
];
|
||||||
|
|
||||||
|
return new JSONResponse($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function deleteClient(int $id): JSONResponse {
|
||||||
* @param int $id
|
|
||||||
* @return RedirectResponse
|
|
||||||
*/
|
|
||||||
public function deleteClient($id) {
|
|
||||||
$client = $this->clientMapper->getByUid($id);
|
$client = $this->clientMapper->getByUid($id);
|
||||||
$this->accessTokenMapper->deleteByClientId($id);
|
$this->accessTokenMapper->deleteByClientId($id);
|
||||||
$this->defaultTokenMapper->deleteByName($client->getName());
|
$this->defaultTokenMapper->deleteByName($client->getName());
|
||||||
$this->clientMapper->delete($client);
|
$this->clientMapper->delete($client);
|
||||||
return new RedirectResponse($this->urlGenerator->getAbsoluteURL('/index.php/settings/admin/security'));
|
return new JSONResponse([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClients(): JSONResponse {
|
||||||
|
$clients = $this->clientMapper->getClients();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($clients as $client) {
|
||||||
|
$result[] = [
|
||||||
|
'id' => $client->getId(),
|
||||||
|
'name' => $client->getName(),
|
||||||
|
'redirectUri' => $client->getRedirectUri(),
|
||||||
|
'clientId' => $client->getClientIdentifier(),
|
||||||
|
'clientSecret' => $client->getSecret(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JSONResponse($result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
/**
|
/**
|
||||||
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
|
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
|
||||||
*
|
*
|
||||||
|
@ -21,46 +22,25 @@
|
||||||
|
|
||||||
namespace OCA\OAuth2\Settings;
|
namespace OCA\OAuth2\Settings;
|
||||||
|
|
||||||
use OCA\OAuth2\Db\ClientMapper;
|
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use OCP\Settings\ISettings;
|
use OCP\Settings\ISettings;
|
||||||
|
|
||||||
class Admin implements ISettings {
|
class Admin implements ISettings {
|
||||||
/** @var ClientMapper */
|
|
||||||
private $clientMapper;
|
|
||||||
|
|
||||||
/**
|
public function getForm(): TemplateResponse {
|
||||||
* @param ClientMapper $clientMapper
|
|
||||||
*/
|
|
||||||
public function __construct(ClientMapper $clientMapper) {
|
|
||||||
$this->clientMapper = $clientMapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return TemplateResponse
|
|
||||||
*/
|
|
||||||
public function getForm() {
|
|
||||||
return new TemplateResponse(
|
return new TemplateResponse(
|
||||||
'oauth2',
|
'oauth2',
|
||||||
'admin',
|
'admin',
|
||||||
[
|
[],
|
||||||
'clients' => $this->clientMapper->getClients(),
|
|
||||||
],
|
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getSection(): string {
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getSection() {
|
|
||||||
return 'security';
|
return 'security';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getPriority(): int {
|
||||||
* {@inheritdoc}
|
|
||||||
*/
|
|
||||||
public function getPriority() {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"name": "oauth2",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "OAuth2 setup",
|
||||||
|
"author": "Roeland Jago Douma <roeland@famdouma.nl>",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"main": "init.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib",
|
||||||
|
"test": "tests"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"dev": "webpack --config src/webpack.dev.js",
|
||||||
|
"watch": "webpack --progress --watch --config src/webpack.dev.js",
|
||||||
|
"build": "webpack --progress --hide-modules --config src/webpack.prod.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"vue": "^2.5.16"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"css-loader": "^0.28.11",
|
||||||
|
"file-loader": "^1.1.11",
|
||||||
|
"vue-loader": "^15.2.4",
|
||||||
|
"vue-template-compiler": "^2.5.16",
|
||||||
|
"webpack": "^4.11.1",
|
||||||
|
"webpack-cli": "^3.0.3",
|
||||||
|
"webpack-merge": "^4.1.2"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<!--
|
||||||
|
- @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
-
|
||||||
|
- @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
-
|
||||||
|
- @license GNU AGPL version 3 or any later version
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as
|
||||||
|
- published by the Free Software Foundation, either version 3 of the
|
||||||
|
- License, or (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div id="oauth2" class="section">
|
||||||
|
<h2>{{ t('oauth2', 'OAuth 2.0 clients') }}</h2>
|
||||||
|
<p class="settings-hint">{{ t('oauth2', 'OAuth 2.0 allows external services to request access to {instanceName}.', { instanceName: oc_defaults.name}) }}</p>
|
||||||
|
<table class="grid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th id="headerName" scope="col">{{ t('oauth2', 'Name') }}</th>
|
||||||
|
<th id="headerRedirectUri" scope="col">{{ t('oauth2', 'Redirection URI') }}</th>
|
||||||
|
<th id="headerClientIdentifier" scope="col">{{ t('oauth2', 'Client Identifier') }}</th>
|
||||||
|
<th id="headerSecret" scope="col">{{ t('oauth2', 'Secret') }}</th>
|
||||||
|
<th id="headerRemove"> </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<OAuthItem v-for="client in clients"
|
||||||
|
:key="client.id"
|
||||||
|
:client="client"
|
||||||
|
@delete="deleteClient"
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<h3>{{ t('oauth2', 'Add client') }}</h3>
|
||||||
|
<form @submit.prevent="addClient">
|
||||||
|
<input type="text" id="name" name="name" :placeholder="t('oauth2', 'Name')" v-model="newClient.name">
|
||||||
|
<input type="url" id="redirectUri" name="redirectUri" :placeholder="t('oauth2', 'Redirection URI')" v-model="newClient.redirectUri">
|
||||||
|
<input type="submit" class="button" :value="t('oauth2', 'Add')">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import OAuthItem from './components/OAuthItem';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
OAuthItem
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
clients: [],
|
||||||
|
newClient: {
|
||||||
|
name: '',
|
||||||
|
redirectUri: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeMount: function() {
|
||||||
|
let requestToken = OC.requestToken;
|
||||||
|
let tokenHeaders = { headers: { requesttoken: requestToken } };
|
||||||
|
|
||||||
|
axios.get(OC.generateUrl('apps/oauth2/clients'), tokenHeaders)
|
||||||
|
.then((response) => {
|
||||||
|
this.clients = response.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
deleteClient(id) {
|
||||||
|
let requestToken = OC.requestToken;
|
||||||
|
let tokenHeaders = { headers: { requesttoken: requestToken } };
|
||||||
|
|
||||||
|
axios.delete(OC.generateUrl('apps/oauth2/clients/{id}', {id: id}), tokenHeaders)
|
||||||
|
.then((response) => {
|
||||||
|
this.clients = this.clients.filter(client => client.id !== id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addClient() {
|
||||||
|
let requestToken = OC.requestToken;
|
||||||
|
let tokenHeaders = { headers: { requesttoken: requestToken } };
|
||||||
|
|
||||||
|
axios.post(
|
||||||
|
OC.generateUrl('apps/oauth2/clients'),
|
||||||
|
{
|
||||||
|
name: this.newClient.name,
|
||||||
|
redirectUri: this.newClient.redirectUri
|
||||||
|
},
|
||||||
|
tokenHeaders)
|
||||||
|
.then((response) => {
|
||||||
|
this.clients.push(response.data)
|
||||||
|
|
||||||
|
this.newClient.name = '';
|
||||||
|
this.newClient.redirectUri = '';
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<!--
|
||||||
|
- @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
-
|
||||||
|
- @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
-
|
||||||
|
- @license GNU AGPL version 3 or any later version
|
||||||
|
-
|
||||||
|
- This program is free software: you can redistribute it and/or modify
|
||||||
|
- it under the terms of the GNU Affero General Public License as
|
||||||
|
- published by the Free Software Foundation, either version 3 of the
|
||||||
|
- License, or (at your option) any later version.
|
||||||
|
-
|
||||||
|
- This program is distributed in the hope that it will be useful,
|
||||||
|
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
- GNU Affero General Public License for more details.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the GNU Affero General Public License
|
||||||
|
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<tr>
|
||||||
|
<td>{{name}}</td>
|
||||||
|
<td>{{redirectUri}}</td>
|
||||||
|
<td><code>{{clientId}}</code></td>
|
||||||
|
<td><code>{{renderedSecret}}</code><a class='icon-toggle has-tooltip' :title="t('oauth2', 'Show client secret')" @click="toggleSecret"></a></td>
|
||||||
|
<td class="action-column"><span><a class="icon-delete has-tooltip" :title="t('oauth2', 'Delete')" @click="$emit('delete', id)"></a></span></td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'OAuthItem',
|
||||||
|
props: {
|
||||||
|
client: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
id: this.client.id,
|
||||||
|
name: this.client.name,
|
||||||
|
redirectUri: this.client.redirectUri,
|
||||||
|
clientId: this.client.clientId,
|
||||||
|
clientSecret: this.client.clientSecret,
|
||||||
|
renderSecret: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
renderedSecret: function() {
|
||||||
|
if (this.renderSecret) {
|
||||||
|
return this.clientSecret;
|
||||||
|
} else {
|
||||||
|
return '****';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleSecret() {
|
||||||
|
this.renderSecret = !this.renderSecret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,35 @@
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2018 Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
*
|
||||||
|
* @author Roeland Jago Douma <roeland@famdouma.nl>
|
||||||
|
*
|
||||||
|
* @license GNU AGPL version 3 or any later version
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Vue from 'vue';
|
||||||
|
import App from './App.vue';
|
||||||
|
|
||||||
|
Vue.prototype.t = t;
|
||||||
|
Vue.prototype.oc_defaults = oc_defaults;
|
||||||
|
Vue.prototype.OC = OC;
|
||||||
|
|
||||||
|
const app = new Vue({
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#oauth2');
|
||||||
|
|
||||||
|
export { app };
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
const path = require('path')
|
||||||
|
const { VueLoaderPlugin } = require('vue-loader');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: path.join(__dirname, 'main.js'),
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../js'),
|
||||||
|
publicPath: '/js',
|
||||||
|
filename: 'oauth2.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: 'vue-loader',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new VueLoaderPlugin()
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'vue$': 'vue/dist/vue.esm.js'
|
||||||
|
},
|
||||||
|
extensions: ['*', '.js', '.vue', '.json']
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
const merge = require('webpack-merge');
|
||||||
|
const common = require('./webpack.common.js');
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'development',
|
||||||
|
devServer: {
|
||||||
|
historyApiFallback: true,
|
||||||
|
noInfo: true,
|
||||||
|
overlay: true
|
||||||
|
},
|
||||||
|
devtool: '#eval-source-map',
|
||||||
|
})
|
|
@ -0,0 +1,7 @@
|
||||||
|
const merge = require('webpack-merge')
|
||||||
|
const common = require('./webpack.common.js')
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
mode: 'production',
|
||||||
|
devtool: '#source-map'
|
||||||
|
})
|
|
@ -19,58 +19,9 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$urlGenerator = \OC::$server->getURLGenerator();
|
script('oauth2', 'oauth2');
|
||||||
$themingDefaults = \OC::$server->getThemingDefaults();
|
|
||||||
|
|
||||||
script('oauth2', 'setting-admin');
|
|
||||||
style('oauth2', 'setting-admin');
|
style('oauth2', 'setting-admin');
|
||||||
|
|
||||||
/** @var array $_ */
|
|
||||||
/** @var \OCA\OAuth2\Db\Client[] $clients */
|
|
||||||
$clients = $_['clients'];
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="oauth2" class="section">
|
<div id="oauth2"></div>
|
||||||
<h2><?php p($l->t('OAuth 2.0 clients')); ?></h2>
|
|
||||||
<p class="settings-hint"><?php p($l->t('OAuth 2.0 allows external services to request access to %s.', [$themingDefaults->getName()])); ?></p>
|
|
||||||
|
|
||||||
<table class="grid">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th id="headerName" scope="col"><?php p($l->t('Name')); ?></th>
|
|
||||||
<th id="headerRedirectUri" scope="col"><?php p($l->t('Redirection URI')); ?></th>
|
|
||||||
<th id="headerClientIdentifier" scope="col"><?php p($l->t('Client Identifier')); ?></th>
|
|
||||||
<th id="headerSecret" scope="col"><?php p($l->t('Secret')); ?></th>
|
|
||||||
<th id="headerRemove"> </th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php
|
|
||||||
$imageUrl = $urlGenerator->imagePath('core', 'actions/toggle.svg');
|
|
||||||
foreach ($clients as $client) {
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?php p($client->getName()); ?></td>
|
|
||||||
<td><?php p($client->getRedirectUri()); ?></td>
|
|
||||||
<td><code><?php p($client->getClientIdentifier()); ?></code></td>
|
|
||||||
<td data-value="<?php p($client->getSecret()); ?>"><code>****</code><img class='show-oauth-credentials' src="<?php p($imageUrl); ?>"/></td>
|
|
||||||
<td>
|
|
||||||
<form id="form-inline" class="delete" action="<?php p($urlGenerator->linkToRoute('oauth2.Settings.deleteClient', ['id' => $client->getId()])); ?>" method="POST">
|
|
||||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
|
||||||
<input type="submit" class="button icon-delete" value="">
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php } ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<br/>
|
|
||||||
<h3><?php p($l->t('Add client')); ?></h3>
|
|
||||||
<form action="<?php p($urlGenerator->linkToRoute('oauth2.Settings.addClient')); ?>" method="POST">
|
|
||||||
<input type="text" id="name" name="name" placeholder="<?php p($l->t('Name')); ?>">
|
|
||||||
<input type="url" id="redirectUri" name="redirectUri" placeholder="<?php p($l->t('Redirection URI')); ?>">
|
|
||||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
|
||||||
<input type="submit" class="button" value="<?php p($l->t('Add')); ?>">
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -26,17 +26,14 @@ use OCA\OAuth2\Controller\SettingsController;
|
||||||
use OCA\OAuth2\Db\AccessTokenMapper;
|
use OCA\OAuth2\Db\AccessTokenMapper;
|
||||||
use OCA\OAuth2\Db\Client;
|
use OCA\OAuth2\Db\Client;
|
||||||
use OCA\OAuth2\Db\ClientMapper;
|
use OCA\OAuth2\Db\ClientMapper;
|
||||||
use OCP\AppFramework\Http\RedirectResponse;
|
use OCP\AppFramework\Http\JSONResponse;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IURLGenerator;
|
|
||||||
use OCP\Security\ISecureRandom;
|
use OCP\Security\ISecureRandom;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
|
||||||
class SettingsControllerTest extends TestCase {
|
class SettingsControllerTest extends TestCase {
|
||||||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $request;
|
private $request;
|
||||||
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
|
|
||||||
private $urlGenerator;
|
|
||||||
/** @var ClientMapper|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var ClientMapper|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $clientMapper;
|
private $clientMapper;
|
||||||
/** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
@ -52,7 +49,6 @@ class SettingsControllerTest extends TestCase {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->request = $this->createMock(IRequest::class);
|
$this->request = $this->createMock(IRequest::class);
|
||||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
|
||||||
$this->clientMapper = $this->createMock(ClientMapper::class);
|
$this->clientMapper = $this->createMock(ClientMapper::class);
|
||||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||||
$this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
|
$this->accessTokenMapper = $this->createMock(AccessTokenMapper::class);
|
||||||
|
@ -61,7 +57,6 @@ class SettingsControllerTest extends TestCase {
|
||||||
$this->settingsController = new SettingsController(
|
$this->settingsController = new SettingsController(
|
||||||
'oauth2',
|
'oauth2',
|
||||||
$this->request,
|
$this->request,
|
||||||
$this->urlGenerator,
|
|
||||||
$this->clientMapper,
|
$this->clientMapper,
|
||||||
$this->secureRandom,
|
$this->secureRandom,
|
||||||
$this->accessTokenMapper,
|
$this->accessTokenMapper,
|
||||||
|
@ -90,27 +85,39 @@ class SettingsControllerTest extends TestCase {
|
||||||
$this->clientMapper
|
$this->clientMapper
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('insert')
|
->method('insert')
|
||||||
->with($client);
|
->with($this->callback(function (Client $c) {
|
||||||
|
return $c->getName() === 'My Client Name' &&
|
||||||
|
$c->getRedirectUri() === 'https://example.com/' &&
|
||||||
|
$c->getSecret() === 'MySecret' &&
|
||||||
|
$c->getClientIdentifier() === 'MyClientIdentifier';
|
||||||
|
}))->will($this->returnCallback(function (Client $c) {
|
||||||
|
$c->setId(42);
|
||||||
|
return $c;
|
||||||
|
}));
|
||||||
|
|
||||||
$this->urlGenerator
|
$result = $this->settingsController->addClient('My Client Name', 'https://example.com/');
|
||||||
->expects($this->once())
|
$this->assertInstanceOf(JSONResponse::class, $result);
|
||||||
->method('getAbsoluteURL')
|
|
||||||
->with('/index.php/settings/admin/security')
|
|
||||||
->willReturn('https://example.com/index.php/settings/admin/security');
|
|
||||||
|
|
||||||
$expected = new RedirectResponse('https://example.com/index.php/settings/admin/security');
|
$data = $result->getData();
|
||||||
$this->assertEquals($expected, $this->settingsController->addClient('My Client Name', 'https://example.com/'));
|
|
||||||
|
$this->assertEquals([
|
||||||
|
'id' => 42,
|
||||||
|
'name' => 'My Client Name',
|
||||||
|
'redirectUri' => 'https://example.com/',
|
||||||
|
'clientId' => 'MyClientIdentifier',
|
||||||
|
'clientSecret' => 'MySecret',
|
||||||
|
], $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeleteClient() {
|
public function testDeleteClient() {
|
||||||
$client = new Client();
|
$client = new Client();
|
||||||
|
$client->setId(123);
|
||||||
$client->setName('My Client Name');
|
$client->setName('My Client Name');
|
||||||
$client->setRedirectUri('https://example.com/');
|
$client->setRedirectUri('https://example.com/');
|
||||||
$client->setSecret('MySecret');
|
$client->setSecret('MySecret');
|
||||||
$client->setClientIdentifier('MyClientIdentifier');
|
$client->setClientIdentifier('MyClientIdentifier');
|
||||||
|
|
||||||
$this->clientMapper
|
$this->clientMapper
|
||||||
->expects($this->at(0))
|
|
||||||
->method('getByUid')
|
->method('getByUid')
|
||||||
->with(123)
|
->with(123)
|
||||||
->willReturn($client);
|
->willReturn($client);
|
||||||
|
@ -123,17 +130,52 @@ class SettingsControllerTest extends TestCase {
|
||||||
->method('deleteByName')
|
->method('deleteByName')
|
||||||
->with('My Client Name');
|
->with('My Client Name');
|
||||||
$this->clientMapper
|
$this->clientMapper
|
||||||
->expects($this->at(1))
|
|
||||||
->method('delete')
|
->method('delete')
|
||||||
->with($client);
|
->with($client);
|
||||||
|
|
||||||
$this->urlGenerator
|
$result = $this->settingsController->deleteClient(123);
|
||||||
->expects($this->once())
|
$this->assertInstanceOf(JSONResponse::class, $result);
|
||||||
->method('getAbsoluteURL')
|
$this->assertEquals([], $result->getData());
|
||||||
->with('/index.php/settings/admin/security')
|
}
|
||||||
->willReturn('https://example.com/index.php/settings/admin/security');
|
|
||||||
|
|
||||||
$expected = new RedirectResponse('https://example.com/index.php/settings/admin/security');
|
public function testGetClients() {
|
||||||
$this->assertEquals($expected, $this->settingsController->deleteClient(123));
|
$client1 = new Client();
|
||||||
|
$client1->setId(123);
|
||||||
|
$client1->setName('My Client Name');
|
||||||
|
$client1->setRedirectUri('https://example.com/');
|
||||||
|
$client1->setSecret('MySecret');
|
||||||
|
$client1->setClientIdentifier('MyClientIdentifier');
|
||||||
|
|
||||||
|
$client2 = new Client();
|
||||||
|
$client2->setId(42);
|
||||||
|
$client2->setName('My Client Name2');
|
||||||
|
$client2->setRedirectUri('https://example.com/2');
|
||||||
|
$client2->setSecret('MySecret2');
|
||||||
|
$client2->setClientIdentifier('MyClientIdentifier2');
|
||||||
|
|
||||||
|
$this->clientMapper->method('getClients')
|
||||||
|
->willReturn([$client1, $client2]);
|
||||||
|
|
||||||
|
$result = $this->settingsController->getClients();
|
||||||
|
$this->assertInstanceOf(JSONResponse::class, $result);
|
||||||
|
|
||||||
|
$data = $result->getData();
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
[
|
||||||
|
'id' => 123,
|
||||||
|
'name' => 'My Client Name',
|
||||||
|
'redirectUri' => 'https://example.com/',
|
||||||
|
'clientId' => 'MyClientIdentifier',
|
||||||
|
'clientSecret' => 'MySecret',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 42,
|
||||||
|
'name' => 'My Client Name2',
|
||||||
|
'redirectUri' => 'https://example.com/2',
|
||||||
|
'clientId' => 'MyClientIdentifier2',
|
||||||
|
'clientSecret' => 'MySecret2',
|
||||||
|
],
|
||||||
|
], $data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,35 +21,25 @@
|
||||||
|
|
||||||
namespace OCA\OAuth2\Tests\Settings;
|
namespace OCA\OAuth2\Tests\Settings;
|
||||||
|
|
||||||
use OCA\OAuth2\Db\ClientMapper;
|
|
||||||
use OCA\OAuth2\Settings\Admin;
|
use OCA\OAuth2\Settings\Admin;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
|
||||||
class AdminTest extends TestCase {
|
class AdminTest extends TestCase {
|
||||||
/** @var ClientMapper|\PHPUnit_Framework_MockObject_MockObject */
|
|
||||||
private $clientMapper;
|
|
||||||
/** @var Admin|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var Admin|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $admin;
|
private $admin;
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->clientMapper = $this->createMock(ClientMapper::class);
|
$this->admin = new Admin();
|
||||||
$this->admin = new Admin($this->clientMapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetForm() {
|
public function testGetForm() {
|
||||||
$this->clientMapper
|
|
||||||
->expects($this->once())
|
|
||||||
->method('getClients')
|
|
||||||
->willReturn(['MyClients']);
|
|
||||||
|
|
||||||
$expected = new TemplateResponse(
|
$expected = new TemplateResponse(
|
||||||
'oauth2',
|
'oauth2',
|
||||||
'admin',
|
'admin',
|
||||||
[
|
[
|
||||||
'clients' => ['MyClients'],
|
|
||||||
],
|
],
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue