Commit c2c00fca by Johannes Zellner

Add access token ui and rest api

1 parent d5940df0
......@@ -89,3 +89,12 @@ a:hover, a:focus {
align-items: center;
justify-content: center;
}
.access-token-input {
padding: 5px 0;
width: 450px;
}
.access-token-input > input, .access-token-input i {
cursor: copy !important;
}
......@@ -36,6 +36,21 @@
</span>
</el-dialog>
<el-dialog title="Access Tokens" :visible.sync="accessTokensDialogVisible" width="30%">
Tokens can be used with the surfer <a href="https://www.npmjs.com/package/cloudron-surfer" target="_blank">cli tool</a> or using the Api directly.
They are shared between all users.
<br/>
<br/>
<div>
<div v-for="accessToken in accessTokens">
<el-input suffix-icon="el-icon-copy-document" v-model="accessToken" class="access-token-input" @focus="onCopyAccessToken" size="small"></el-input>
<el-button icon="el-icon-delete" type="danger" size="small" @click="onDeleteAccessToken(accessToken)"></el-button>
</div>
</div>
<br/>
<el-button @click="onCreateAccessToken()" size="small" type="primary">Create Access Token</el-button>
</el-dialog>
<el-header>
<el-row type="flex" justify="space-between">
<div style="padding: 7px;">
......@@ -66,6 +81,7 @@
</el-dropdown-item>
<el-dropdown-item disabled divided>WebDAV Endpoint</el-dropdown-item>
<el-dropdown-item><a href="/_webdav/" target="_blank">{{ origin }}/_webdav/</a></el-dropdown-item>
<el-dropdown-item command="apiAccess" divided><i class="el-icon-connection"></i> Access Tokens</el-dropdown-item>
<el-dropdown-item command="about" divided><i class="el-icon-info"></i> About</el-dropdown-item>
<el-dropdown-item command="logout" id="logoutButton"><i class="el-icon-circle-close"></i> Logout</el-dropdown-item>
</el-dropdown-menu>
......
......@@ -43,6 +43,8 @@ function initWithToken(accessToken) {
app.folderListingEnabled = !!result.body.folderListingEnabled;
loadDirectory(decode(window.location.hash.slice(1)));
app.refreshAccessTokens();
});
});
}
......@@ -278,7 +280,9 @@ var app = new Vue({
password: '',
busy: false
},
entries: []
entries: [],
accessTokens: [],
accessTokensDialogVisible: false
},
methods: {
onLogin: function () {
......@@ -312,6 +316,8 @@ var app = new Vue({
}).then(function () {}).catch(function () {});
} else if (command === 'logout') {
logout();
} else if (command === 'apiAccess') {
this.accessTokensDialogVisible = true;
}
},
onDownload: function (entry) {
......@@ -415,6 +421,42 @@ var app = new Vue({
});
}).catch(function () {});
},
refreshAccessTokens: function () {
var that = this;
superagent.get('/api/tokens').query({ access_token: localStorage.accessToken }).end(function (error, result) {
if (error && !result) return that.$message.error(error.message);
that.accessTokens = result.body.accessTokens;
});
},
onCopyAccessToken: function (event) {
event.target.select();
document.execCommand('copy');
this.$message({ type: 'success', message: 'Access token copied to clipboard' });
},
onCreateAccessToken: function () {
var that = this;
superagent.post('/api/tokens').query({ access_token: localStorage.accessToken }).end(function (error, result) {
if (error && !result) return that.$message.error(error.message);
that.refreshAccessTokens();
});
},
onDeleteAccessToken: function (token) {
var that = this;
this.$confirm('All actions from apps using this token will fail!', 'Really delete this access token?', { confirmButtonText: 'Yes Delete', cancelButtonText: 'No' }).then(function () {
superagent.delete('/api/tokens/' + token).query({ access_token: localStorage.accessToken }).end(function (error, result) {
if (error && !result) return that.$message.error(error.message);
that.refreshAccessTokens();
});
}).catch(function () {});
},
prettyDate: function (row, column, cellValue, index) {
var date = new Date(cellValue),
diff = (((new Date()).getTime() - date.getTime()) / 1000),
......
......@@ -77,6 +77,9 @@ router.post ('/api/login', auth.login);
router.post ('/api/logout', auth.verify, auth.logout);
router.get ('/api/settings', auth.verify, getSettings);
router.put ('/api/settings', auth.verify, setSettings);
router.get ('/api/tokens', auth.verify, auth.getTokens);
router.post ('/api/tokens', auth.verify, auth.createToken);
router.delete('/api/tokens/:token', auth.verify, auth.delToken);
router.get ('/api/profile', auth.verify, auth.getProfile);
router.get ('/api/files/*', auth.verify, files.get);
router.post ('/api/files/*', auth.verify, multipart, files.post);
......
......@@ -15,6 +15,8 @@ const LDAP_USERS_BASE_DN = process.env.CLOUDRON_LDAP_USERS_BASE_DN;
const LOCAL_AUTH_FILE = path.resolve(process.env.LOCAL_AUTH_FILE || './.users.json');
const TOKENSTORE_FILE = path.resolve(process.env.TOKENSTORE_FILE || './.tokens.json');
const AUTH_METHOD = (LDAP_URL && LDAP_USERS_BASE_DN) ? 'ldap' : 'local';
const LOGIN_TOKEN_PREFIX = 'login-';
const API_TOKEN_PREFIX = 'api-';
if (AUTH_METHOD === 'ldap') {
console.log('Use ldap auth');
......@@ -34,8 +36,11 @@ var tokenStore = {
get: function (token, callback) {
callback(tokenStore.data[token] ? null : 'not found', tokenStore.data[token]);
},
set: function (token, data, callback) {
tokenStore.data[token] = data;
getApiTokens: function (callback) {
callback(null, Object.keys(tokenStore.data).filter(function (t) { return t.indexOf(API_TOKEN_PREFIX) === 0; }))
},
set: function (token, user, callback) {
tokenStore.data[token] = user;
tokenStore.save();
callback(null);
},
......@@ -102,7 +107,7 @@ exports.login = function (req, res, next) {
verifyUser(req.body.username, req.body.password, function (error, user) {
if (error) return next(new HttpError(401, 'Invalid credentials'));
var accessToken = uuid();
var accessToken = LOGIN_TOKEN_PREFIX + uuid();
tokenStore.set(accessToken, user, function (error) {
if (error) return next(new HttpError(500, error));
......@@ -139,6 +144,32 @@ exports.getProfile = function (req, res, next) {
next(new HttpSuccess(200, { username: req.user.username }));
};
exports.getTokens = function (req, res, next) {
tokenStore.getApiTokens(function (error, result) {
if (error) return next(new HttpError(500, error));
next(new HttpSuccess(200, { accessTokens: result }));
});
};
exports.createToken = function (req, res, next) {
var accessToken = API_TOKEN_PREFIX + uuid();
tokenStore.set(accessToken, req.user, function (error) {
if (error) return next(new HttpError(500, error));
next(new HttpSuccess(201, { accessToken: accessToken }));
});
};
exports.delToken = function (req, res, next) {
tokenStore.del(req.params.token, function (error) {
if (error) console.error(error);
next(new HttpSuccess(200, {}));
});
};
// webdav usermanager
exports.WebdavUserManager = WebdavUserManager;
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!