@universis/user-storage
Version:
Universis Api Server Extension for managing user remote storage
392 lines (321 loc) • 10.2 kB
JavaScript
;
var _app = require("@themost/common/app");
var _common = require("@themost/common");
var _express = require("@themost/express");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class UserStorageAccessConfiguration extends _common.ConfigurationStrategy {
/**
* @param {ConfigurationBase} configuration
*/
constructor(configuration) {
super(configuration);
let elements = []; // define property
Object.defineProperty(this, 'elements', {
get: () => {
return elements;
},
enumerable: true
});
}
/**
*
* @param {DataContext} context - The underlying data context
* @param {string} key - A string which represents the requested key
* @param {string} access - A string which represents the requested access type (read or write)
*/
async verify(context, key, access) {
// validate request context
_common.Args.notNull(context, 'Context'); // validate request context user
_common.Args.notNull(context.user, 'User');
if (context.user.authenticationScope && context.user.authenticationScope.length > 0) {
// get user context scopes as array e.g, ['students', 'students:read']
let contextScopes = context.user.authenticationScope.split(','); // get user access based on HTTP method e.g. GET -> read access
return this.elements.find(x => {
// filter element by access level
return x.access.indexOf(access) >= 0 // resource path
&& new RegExp("^" + x.resource, 'i').test(key) // and scopes
&& x.scope.find(y => {
// search user scopes (validate wildcard scope)
return y === "*" || contextScopes.indexOf(y) >= 0;
});
});
}
}
}
class DefaultUserStorageAccessConfiguration extends UserStorageAccessConfiguration {
/**
* @param {ConfigurationBase} configuration
*/
constructor(configuration) {
super(configuration);
let defaults = []; // load scope access from configuration resource
try {
/**
* @type {Array<*>}
*/
defaults = require(_path.default.resolve(configuration.getConfigurationPath(), 'user.storage.access.json'));
} catch (err) {
// if an error occurred other than module not found (there are no default access policies)
if (err.code !== 'MODULE_NOT_FOUND') {
// throw error
throw err;
} // otherwise continue
_common.TraceUtils.error('Default user storage access configuration cannot be found. You may configure user storage access manually or use another user storage access strategy to give user access to read or write to storage.');
}
this.elements.push.apply(this.elements, defaults);
}
}
class UserStorageService extends _app.ApplicationService {
/**
* Formats a path like key string to a redis command compatible key e.g. user1/application1/lastAction to .user1.application1.lastAction
* @param key
* @returns {string|*}
*/
static escapeKey(key) {
let res = key.replace(/\//ig, '.');
if (/^\./.test(res)) {
return res;
}
return '.' + res;
}
/**
* @param {IApplication} app
*/
constructor(app) {
super(app); // get redis options
this.options = Object.assign({}, app.getConfiguration().getSourceAt('settings/universis/storage/options')); //register default user storage access configuration
app.getConfiguration().useStrategy(UserStorageAccessConfiguration, DefaultUserStorageAccessConfiguration); // extend service router
/**
* @swagger
*
* /api/users/me/storage/get:
* post:
* tags:
* - User
* description: Returns a user storage item based on the specified key path
* security:
* - OAuth2:
* - registrar
* - teachers
* - students
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* key:
* type: string
* required:
* - key
* responses:
* '200':
* description: success
* content:
* application/json:
* schema:
* type: object
* '400':
* description: bad request
* '403':
* description: forbidden
* '404':
* description: not found
* '500':
* description: internal server error
*/
_express.serviceRouter.post('/users/me/storage/get', async (req, res, next) => {
try {
if (req.body == null) {
return next(new _common.HttpBadRequestError('Request body is missing'));
}
if (typeof req.body.key !== 'string') {
return next(new _common.HttpBadRequestError('Request key parameter is missing'));
}
const userStorage = UserStorage.create(req.context);
const result = await userStorage.getItem(req.body.key);
return res.json({
value: result
});
} catch (err) {
return next(err);
}
});
/**
* @swagger
*
* /api/users/me/storage/set:
* post:
* tags:
* - User
* description: Sets a user storage item to the specified key path.
* security:
* - OAuth2:
* - registrar
* - teachers
* - students
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* key:
* type: string
* value:
* type: object
* required:
* - key
* - value
* responses:
* '200':
* description: success
* content:
* application/json:
* schema:
* type: object
* '400':
* description: bad request
* '403':
* description: forbidden
* '404':
* description: not found
* '500':
* description: internal server error
*/
_express.serviceRouter.post('/users/me/storage/set', async (req, res, next) => {
try {
if (req.body == null) {
return next(new _common.HttpBadRequestError('Request body is missing'));
}
if (typeof req.body.key !== 'string') {
return next(new _common.HttpBadRequestError('Request key parameter is missing'));
}
const userStorage = UserStorage.create(req.context); // if value is null remove key
let result;
if (req.body.value == null) {
result = await userStorage.removeItem(req.body.key); // return true if key has been removed or false if key is missing
return res.json({
value: result
});
} // otherwise set item
result = await userStorage.setItem(req.body.key, req.body.value);
return res.json({
value: result
});
} catch (err) {
return next(err);
}
});
}
/**
* @name RedisClient#json_set
* @param {*} ...args
*/
/**
* @name RedisClient#json_get
* @param {*} ...args
*/
// noinspection JSMethodCanBeStatic
/**
* @abstract
* @param {DataContext} context
* @param {string} key
* @param {*} value
* @param {number=} expiration
*/
// eslint-disable-next-line no-unused-vars
async setItem(context, key, value, expiration) {
throw new _common.AbstractMethodError();
} // noinspection JSMethodCanBeStatic
/**
* @abstract
* @param {DataContext} context
* @param {string} key
*/
// eslint-disable-next-line no-unused-vars
async getItem(context, key) {
throw new _common.AbstractMethodError();
} // noinspection JSMethodCanBeStatic
/**
* @abstract
* @param {DataContext} context
* @param {string} key
* @return Promise<*>
*/
// eslint-disable-next-line no-unused-vars
async removeItem(context, key) {
throw new _common.AbstractMethodError();
} // noinspection JSMethodCanBeStatic
/**
* @abstract
* @param {DataContext} context
*/
// eslint-disable-next-line no-unused-vars
async clear(context) {
throw new _common.AbstractMethodError();
}
}
/**
* @name UserStorage#context
* @type {DataContext}
*
*/
/**
* @class
*/
class UserStorage {
constructor() {}
/**
* @param context
* @returns {UserStorage}
*/
static create(context) {
const res = new UserStorage();
res.context = context;
return res;
}
/**
* Gets the current user storage service
* @returns {UserStorageService}
*/
getService() {
const userStorageService = this.context.getApplication().getService(UserStorageService);
_common.Args.notNull(userStorageService, UserStorageService.name);
return userStorageService;
}
/**
* Gets an item from user storage
* @param key
* @returns {Promise<*>}
*/
getItem(key) {
return this.getService().getItem(this.context, key);
}
/**
* Sets an item to user remote storage
* @param {string} key
* @param {*} value
* @param {number=} expiration
* @returns {Promise<*>}
*/
setItem(key, value, expiration) {
return this.getService().setItem(this.context, key, value, expiration);
}
/**
* Removes an item from user storage
* @param {string} key
* @returns {Promise<*>}
*/
removeItem(key) {
return this.getService().removeItem(this.context, key);
}
}
module.exports.UserStorageAccessConfiguration = UserStorageAccessConfiguration;
module.exports.DefaultUserStorageAccessConfiguration = DefaultUserStorageAccessConfiguration;
module.exports.UserStorage = UserStorage;
module.exports.UserStorageService = UserStorageService;
//# sourceMappingURL=service.js.map