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;
+}
</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;">
</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>
app.folderListingEnabled = !!result.body.folderListingEnabled;
loadDirectory(decode(window.location.hash.slice(1)));
+
+ app.refreshAccessTokens();
});
});
}
password: '',
busy: false
},
- entries: []
+ entries: [],
+ accessTokens: [],
+ accessTokensDialogVisible: false
},
methods: {
onLogin: function () {
}).then(function () {}).catch(function () {});
} else if (command === 'logout') {
logout();
+ } else if (command === 'apiAccess') {
+ this.accessTokensDialogVisible = true;
}
},
onDownload: function (entry) {
});
}).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),
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);
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');
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);
},
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));
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;