Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
oeCloud framework has been in development for almost two years and several developers for the first time worked on framework development. It has been adopted by many application developers so far and is being popular within Infosys and EdgeVerve. It has been demonstrated that application development using oeCloud is fast and developer get many levers to play with when he/she developing with oeCloud - due to framework offering and also JavaScript inherent power.
However, there is scope of improvement.
To address above concerns, oeCloud is being modularized
oe-cloud is base node module for all oeCloud base application development.
This is most important project of oeCloud. This module needs to be required in application's server.js. Below are responsibilities of oe-cloud
This is basically javascript array of objects that contains list of node_modules that oe-cloud should load as part of application start up. This usually is JSON file with name as app-list.json. However, to achieve programmability, this can be also .js file like app-list.js. Another small improvement is, application developer can maintain different app-list for different environments which is based on NODE_ENV environment variable. If all sources (app-list.json, app-list.js and app-list.<NODE_ENV>.js) exist, oe-cloud will merge these objects. Typical app-list.json, which would be part of application would look like
[
{
"path": "oe-cloud",
"enabled": true
},
{
"path": "oe-module",
"enabled": true
},
{
"path": "oe-common-mixins",
"enabled": true
},
{
"path": "oe-validation",
"enabled": true
},
{
"path": "./",
"enabled": true
}
]
Typically, following code can be written in oeCloud application's server/server.js file
var oecloud = require('oe-cloud');
oecloud.boot(__dirname, function(err){
oecloud.start();
})
Above code should able to start application. you don't have to do require of loopback or any of it's component.
BaseEntity model is part of this module. Mixins in app-list.json modules will be attached to BaseEntity model - as described below. Most of the times, application models will be directly or indirectly derived from BaseEntity. Therefore, all functionalities of BaseEntity model is available to derived model.
ModelDefinition model stores metadata of all models in database. It will further opens up REST end point for client to get metadata of models and also can be used for runtime creation of new model.
Note : There is possibility of loading oe Modules automatically. But having explicitly specified in app-list.json can be better idea and there is no ambiguity.
oe-cloud interacts with node modules defined in app-list.json in very specific, predefined way. Below are specifications
module.exports = function(app){
// oe-cloud will call this function when it loads the module and when init() is not defined.
// app object will be passed which has got much of information to manipulate
// you can set observer hook as below
app.observe('loaded', function(ctx, next){
//ctx.app will have app handle
return next();
});
}
There are two observers provided.
When programmer calls boot, this even is fired to indicates options parameter is prepared. This options parameter will be used to pass to loopback boot. This includes list of all Models, Mixins folders, middleware and so on. Programmmer has opportunity to inspect / change this parameter.
oeCloud boot is two step process. Based on options passed, it prepares instructions. That is first step and then in second steps it executes instructions. By hooking into this observer, programmer has opportunity to inspect and change instructions. This is useful when programmer wants to change behavior of model. For example, programmer may want to apply mixin on some model, or add property to model. Remember, model at this point is not created. At this point, you are just modifying instructions.
oecloud.observe('boot-instructions-prepared', function (ctx, next) {
var models = ctx.instructions.models;
var literalModel = ctx.instructions.models.find(function(item) { return item.name === "Literal" ;})
literalModel.definition.mixins["SomeMixin"] = true;
return next();
})
Note that, mixins are not applied to BaseEntity by default.
oe-cloud can load models defined in app-list.json's node_module.
{
"_meta": {
"sources": [
"../common/models"
],
"mixins": [
"../common/mixins"
]
},
"MyModel" :{
"public" : true,
"dataSource": "db",
}
}
In above model-config, MyModel will be created as public model. However, definition of the model (.json) should be located in common/models folder of module. so in case of data personalization node module, my-model.json file should be present in /node_modules/oe-data-personalization/commmon/models folder
"OeSomeModule" : {
"enable" : true,
"mixins" : {
"MixinA" : false,
"MixinB" : true
}
}
"_meta": {
"sources": [
"../common/models"
],
"mixins": [
"../common/mixins"
],
"mixinProperties" : [
{"MixinA" : false },
{"MixinB" : {"a" : 1, "b" : 2} }
]
},
"OeSomeModule" : {
"enable" : true,
"autoEnableMixins" : true
}
above setting will ensure that all mixins of OeSomeModule are attahced to BaseEntity and therefore attach to all models derived from BaseEntity.
var oecloud = require('oe-cloud');
oecloud.addModuleMixinsToBaseEntity('oe-data-personalization', false);
oecloud.boot(__dirname, function(err){
oecloud.start();
})
As shownn above, data personalization mixins will not be attached to BaseEntity by default. In this scenario, you will have to explicitly attach mixin with another API attachMixinsToBaseEntity
var oecloud = require('oe-cloud');
oecloud.addModuleMixinsToBaseEntity('oe-data-personalization', false);
// adding mixin with name of mixin explicitly. Thus not all mixins in data personalization modules are attached.
oecloud.attachMixinsToBaseEntity('DataPersonalizationMixin');
oecloud.boot(__dirname, function(err){
oecloud.start();
})
,
{
"path": "oe-validation",
"ModelValidationMixin" : false,
"enabled": true
},
// boot script with callback
module.exports = function(app, cb){
// must call cb() otherwise next boot script will not be executed
// should throw error if needed like
// return cb(new Error("something went wrong");
}
// boot script without callback
module.exports = function(app){
// next boot script will be executed when function returns
}
oeCloud would merge all middlewares in all modules defined in app-list.json and ensure execution of middleware. Each middleware should have entry in respective module's middleware.json file.
Middleware should have entry such as below in middleware.json of module's server folder.
session:before": {
"./middleware/populate-context-from-scope": {},
"./middleware/populate-context-from-headers": {}
}
and middleware should have code such as below. This file should reside in server/middleware folder of your node_module.
module.exports = function (options) {
return function(req, res, next) {
return next(); // must call next() otherwise next middlware will not be executed and system will hang.
}
}
Typically, oeCloud based application would have its own models to fulfill business requirements. For example, there would model name Customer that can be used to handle Customer entity. It would help store customer data in actual database table customer and also expose REST API for Customer. When such application or product is to be delivered to client, client would want changes to this default implementation of existing Customer model. Those changes could be
As a developer, all of above changes can be done in customization model. Customization module is nothing but regular node_module. This node_module can be loaded into application by specifing it in app-list.json. oeCloud framework would load these modules in sequence of app-list.json. As a customization developer, you need to add your customized module entry into app-list.json
{
"path": "product-customization-module",
"enabled": true
},
You can create any new models in customization module of yours and if they are mentioned in model-config.json of your module, those models will be loaded in application.
However, if you want to customize any model, you need to add extra property named customModel in model-config.json for customized model entry as shown below.
"Customer": {
"dataSource": "db",
"public": true,
"customModel" : true
},
customModel flag would help identify oecloud framework that you want to customize Customer model. oeCloud framework then merge your customization with original model.
You can also have customer.js file which is then loaded and also you can have mixin.
Note : More importantly exports function all .js files of models are executed in sequence. There should not be any extra executable code apart from that inside module.exports () function.
When we are referring to options, we are referring to second parameter usually we pass to model.find() or model.create() functions. This options parameter flows throughout the loopback pipeline. That is all observer hooks will get context in which it has this options parameter.
model.find({where : {id : "a"}}, {ctx : {somefield : "X"} }, function(err, data){
// results are returned in data
});
model.observe('access', function(ctx, next){
assert(ctx.options.somefield === 'X')
})
This is all good when find() is being called by programmer from javascript code. However when find() is called due to http GET request on model, options parameter is parepared in oe-cloud module. specifically lib/warpper.js's _newCreateOptionsFromRemotingContext() is used to build this options.
options has got most important property ctx. All other properties are not touched by oecloud framework.
This property is now built by http request's callContext.ctx property and basically both are actually same.
therefore if the request object is req and options object is options then
assert(req.callContext.ctx === options.ctx);
With above design, if you change callContext.ctx property in middleware, that will be directly reflected in options.ctx property
callContext property of HTTP request is created by oeCloud framework in one of it's middleware. As said above callContext.ctx is same as options.ctx. you must break this link. With this link if you modify callContext.ctx in middleware or before remote hooks, it will be available in options.ctx field in your observer hooks.
Same way, if you change options.ctx in observer hooks, you will see that in after remote hook.
model.beforeRemote( '**', function( ctx, opts, next) {
ctx.req.callContext.someField = 'x';
next();
});
model.observe('before save', function(ctx, next){
assert(ctx.options.ctx.someField === 'x');
ctx.options.ctx.otherField = 'y'
// never do this as it will break link ---->>> ctx.options.ctx = { otherField : 'y', someField : 'x' }
});
model.afterRemote( '**', function( ctx, opts, next) {
assert(ctx.options.ctx.otherField === 'y');
next();
});
There are simple utility functions which can be used in your module
This utility function checks if given Model is derived from BaseEntity. This will be useful many times in programming. Below is code snippet.
const oecloudUtil = require('oecloud/lib/util');
const loopback = require('loopback');
var customerModel = loopback.findModel('Customer')
console.log("Customer Model is derived from BaseEntity ", oecloudUtil.isBaseEntity(customerModel));
This function merge two objects. Below is usage and examples. Developer can use lodash libray for that also.
const util = require('oecloud/lib/util');
var o1 = { a : "a" };
var o2 = { b : "b" };
var o = util.mergeObjects(o1, o2);
// { a: "a", b : "b" }
var o1 = { a : "a" , c : { d : { e : "e" } } };
var o2 = { b : "b" };
util.mergeObjects(o1, o2);
// { a : "a" , b: "b", c : { d : { e : "e" } } };
var o1 = { a : "a" , c : { d : { e : "e" } } };
var o2 = { a : "b" };
util.mergeObjects(o1, o2);
// { a : "b" , c : { d : { e : "e" } } };
var o1 = { a : [1,2,3] , c : { d : { e : "e" } } };
var o2 = { a : [2,4,5] };
util.mergeObjects(o1, o2);
// { a : [1,2,3,4,5] , c : { d : { e : "e" } } };
This function checks if all modules in app-list satisfies depenency.
This function checks if this is instance based query where primary key of Model is part of where clause. For nesting, it looks only and clause.
const utils = require('oecloud/lib/util');
utils.isInstanceQuery(newCustomerModel, { where: { name: 'x' } }); // false
utils.isInstanceQuery(newCustomerModel, { where: { and: [{ name: 'x' }, { id: 1 }] } }); // true
utils.isInstanceQuery(newCustomerModel, { where: { and: [{ name: 'x' }, { age: 1 }, { and: [{ id: 1 }, {age : 10}]}] } }); // true
utils.isInstanceQuery(newCustomerModel, { where: { and: [{ name: 'x' }, { age: 1 }, { or: [{ id: 1 }, {age : 10}]}] } }); // false
This function gives you id value in your data.
const utils = require('oecloud/lib/util');
var id = utils.getIdValue(CustomerModel, { id: 10, name: "A" }); // id = 10
var id = utils.getIdValue(CustomerModel, { name: "A" }); // id = undefined
Returns id field name of the model. By default "id"
const utils = require('oecloud/lib/util');
var idName = idName(CustomerModel); // returns "id"
These are the APIs are made available on application object of oeCloud. These APIs mostly should be called before boot.
This function will allow application to set http or https server. This way, creating of server can be controlled by application. By default oeCloud will create http server.
const oecloud = require('oe-cloud');
var server = require('http').createServer(oecloud);
oecloud.setServer(server);
This function will boot the application. It will do following typical things, but not limited to that.
const oecloud = require('oe-cloud');
oecloud.boot(__dirname, function(err){
orcloud.start();
})
This function will start web server and starts listening on PORT. Default port is 3000, or it can be set by PORT environment variable. This function will emit application start event 'started' that application can listen on.
const oecloud = require('oe-cloud');
oecloud.start()
oecloud.once('started', function(){
// do something.
});
This function adds context parameter to AccessToken. That way, if this field is set during creation of access token, it will be made avaialble throughout the application context.
const oecloud = require('oe-cloud');
oecloud.addContextField('tenantId', {
type: "string"
});
accessToken.evObserve("before save", function (ctx, next) {
ctx.instance.tenantId = findTenantIdForUser(ctx.instance.userId);
return next();
});
// after above code is done during start up of application, "tenantId" is available throughout context
customerModel.evObserve("access", function(ctx, next){
var context = ctx.options.ctx;
assert (context.tenantId)
})
customerModel.beforeRemote("*", function(req, res.next){
var context = req.callContext;
assert (context.tenantId)
})
This function on app object can be used to remove ForceId of models which are not derived from BaseEntity. User, Role and RoleMapping are typical models, which are not derived from BaseEntity and you want to remove ForceId setting.
var app = require('oe-cloud');
app.removeForceId('User');
app.removeForceId('Role');
app.removeForceId('RoleMapping');
However, please note that, by default above code is executed as part of boot script. Meaning, by default, ForceId is deisabled for User, Role and RoleMapping models. Therefore, you can create User/Role/Rolemapping data by passing id field explicitly. If you want to disable this, you can use disableForceIdForUserModels setting to true in config.json.
About ForceId : In loopback 3, ForceId setting is done on model which is true by default. In this case, user/programmer cannot create a record on model by passing his/her own id. Id is always generated by loopback. To disable this setting, you can use removeForceId call.
This function should be called to set default ACL on BaseEntity Model. in oeCloud 2.x, BaseEntity doesn't have any ACL applied. Therefore, all operations on all models which are derived from BaseEntity are possible. To prevent that, programmer can call this method.
var oecloud = require('oe-cloud');
oecloud.setACLToBaseEntity({
"accessType": "WRITE",
"principalType": "ROLE",
"principalId": "$unauthenticated",
"permission": "DENY"
});
// oecloud.boot() and other code
Remember that this has to be done before other models are loaded - meaning it should be done before you call boot().
As a programmer, you can implement oeCloud observer hooks. The most important hook is 'loaded'. you can use code similar to following to create the observer hook. 'loaded' observer is executed when all modules in app-list.json is loaded. you can change some configuration, add or remove mixins before boot gets executed.
app.observe('loaded', function(ctx, next){
//ctx.app will have app handle
return next();
});
Name | Default | comment |
---|---|---|
disableAboutMe | false | AboutMe API is exposed and any authenticated user can call /api/users/aboutme to know about logged in user |
disableDefaultAuth | false | Authentication mechanism is enabled by default. you can set this value to true to disable it |
enableAuthCookie | false | You can enable Auth cookie to be sent when user login using /api/users/login API. By default it is disabled |
enableForceIdForUserModels | false | ForceId is disabled for Users/Role/RoleMapping models. You can enable it to keep default loopback behavior |
Logged in user can know about him/herself by calling this API. API signature is shown below. If auth cookie is enabled, you don't have to pass access_token if you are using browser.
GET
http://localhost:3000/api/users/aboutme?access_token=<youraccesstoken>
var app = require('oe-cloud');
app.observe('loaded', function(ctx, next){
app.addSettingsToModelDefinition({properties : {_versioning : {type : "boolean", default : false}}});
app.addSettingsToModelDefinition({properties : {HistoryMixin : {type : "boolean", default: false}}});
app.addSettingsToBaseEntity({autoscope:["tenantId"]});
return next();
})
Feature | Exisiting | Proposed |
---|---|---|
Custom Types Register-Email & timestamp | Data Source juggler Change | Model Builder Wrapper oe-cloud |
after access observer notification | DAO Change | Data Source juggler Wrapper oe-cloud |
app-list.json handling | server.js | oe-cloud |
EvObserver | Mixin in file | oe-cloud |
Audit Field | Mixin in file | oe-common-mixins |
Versioning | Mixin in file | oe-common-mixins |
History | Mixin | oe-common-mixins |
Idempotency | Mixin+DAO | Not done |
Soft Delete | Mixin + DAO | oe-common-mixins |
Validations | Mixin | oe-validation |
Expression Support | Mixin | oe-expression |
Model Composite - Implicit and Explicit | DAO Change | DAO Wrapper oe-model-composite |
Data Personalization | Mixin | oe-personalization |
Service Personalization | Mixin+Boot | Boot oe-service=personalization |
Cachinge | Mixin+DAO | DAO Wrapper oe-cache |
FAQs
oe-cloud modularization aka oecloud.io
The npm package oe-cloud receives a total of 22 weekly downloads. As such, oe-cloud popularity was classified as not popular.
We found that oe-cloud demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.