Migrate OAuth Admin settings to vue
Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
parent
7b8063a242
commit
d2d1e8e375
|
@ -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' => [
|
||||
[
|
||||
'name' => 'Settings#addClient',
|
||||
'url' => '/settings',
|
||||
'url' => '/clients',
|
||||
'verb' => 'POST',
|
||||
],
|
||||
[
|
||||
'name' => 'Settings#getClients',
|
||||
'url' => '/clients',
|
||||
'verb' => 'GET',
|
||||
],
|
||||
[
|
||||
'name' => 'Settings#deleteClient',
|
||||
'url' => '/clients/{id}/delete',
|
||||
'verb' => 'POST'
|
||||
'url' => '/clients/{id}',
|
||||
'verb' => 'DELETE'
|
||||
],
|
||||
[
|
||||
'name' => 'LoginRedirector#authorize',
|
||||
|
|
|
@ -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.redirctUri">
|
||||
<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: '',
|
||||
redirctUri: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
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.redirctUri
|
||||
},
|
||||
tokenHeaders)
|
||||
.then((response) => {
|
||||
this.clients.push(response.data)
|
||||
|
||||
this.newClient.name = '';
|
||||
this.newClient.redirctUri = '';
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
</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">SHOW SECRET</a></td>
|
||||
<td class="action-column"><span><a class="icon-delete has-tooltip" :title="t('oauth2', 'Delete')" @click="$emit('delete', id)">DELETE</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'
|
||||
})
|
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);
|
||||
}
|
||||
})
|
||||
|
||||
});
|
|
@ -26,6 +26,7 @@ use OCA\OAuth2\Db\AccessTokenMapper;
|
|||
use OCA\OAuth2\Db\Client;
|
||||
use OCA\OAuth2\Db\ClientMapper;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
|
@ -54,7 +55,7 @@ class SettingsController extends Controller {
|
|||
* @param AccessTokenMapper $accessTokenMapper
|
||||
* @param DefaultTokenMapper $defaultTokenMapper
|
||||
*/
|
||||
public function __construct($appName,
|
||||
public function __construct(string $appName,
|
||||
IRequest $request,
|
||||
IURLGenerator $urlGenerator,
|
||||
ClientMapper $clientMapper,
|
||||
|
@ -70,31 +71,49 @@ class SettingsController extends Controller {
|
|||
$this->defaultTokenMapper = $defaultTokenMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $redirectUri
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function addClient($name,
|
||||
$redirectUri) {
|
||||
public function addClient(string $name,
|
||||
string $redirectUri): JSONResponse {
|
||||
$client = new Client();
|
||||
$client->setName($name);
|
||||
$client->setRedirectUri($redirectUri);
|
||||
$client->setSecret($this->secureRandom->generate(64, self::validChars));
|
||||
$client->setClientIdentifier($this->secureRandom->generate(64, self::validChars));
|
||||
$this->clientMapper->insert($client);
|
||||
return new RedirectResponse($this->urlGenerator->getAbsoluteURL('/index.php/settings/admin/security'));
|
||||
$client = $this->clientMapper->insert($client);
|
||||
|
||||
$result = [
|
||||
'id' => $client->getId(),
|
||||
'name' => $client->getName(),
|
||||
'redirectUri' => $client->getRedirectUri(),
|
||||
'clientId' => $client->getClientIdentifier(),
|
||||
'clientSecret' => $client->getSecret(),
|
||||
];
|
||||
|
||||
return new JSONResponse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function deleteClient($id) {
|
||||
public function deleteClient(int $id): JSONResponse {
|
||||
$client = $this->clientMapper->getByUid($id);
|
||||
$this->accessTokenMapper->deleteByClientId($id);
|
||||
$this->defaultTokenMapper->deleteByName($client->getName());
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,9 +43,7 @@ class Admin implements ISettings {
|
|||
return new TemplateResponse(
|
||||
'oauth2',
|
||||
'admin',
|
||||
[
|
||||
'clients' => $this->clientMapper->getClients(),
|
||||
],
|
||||
[],
|
||||
''
|
||||
);
|
||||
}
|
||||
|
|
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 js-src/webpack.dev.js",
|
||||
"watch": "webpack --progress --watch --config js-src/webpack.dev.js",
|
||||
"build": "webpack --progress --hide-modules --config js-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"
|
||||
}
|
||||
}
|
|
@ -19,58 +19,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
$urlGenerator = \OC::$server->getURLGenerator();
|
||||
$themingDefaults = \OC::$server->getThemingDefaults();
|
||||
|
||||
script('oauth2', 'setting-admin');
|
||||
script('oauth2', 'oauth2');
|
||||
style('oauth2', 'setting-admin');
|
||||
|
||||
/** @var array $_ */
|
||||
/** @var \OCA\OAuth2\Db\Client[] $clients */
|
||||
$clients = $_['clients'];
|
||||
?>
|
||||
|
||||
<div id="oauth2" class="section">
|
||||
<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>
|
||||
<div id="oauth2"></div>
|
||||
|
|
Loading…
Reference in New Issue