Skip to content

Instantly share code, notes, and snippets.

@lbrenman
Last active October 27, 2015 04:29
Show Gist options
  • Save lbrenman/69b3fcec38ce3224f8bd to your computer and use it in GitHub Desktop.
Save lbrenman/69b3fcec38ce3224f8bd to your computer and use it in GitHub Desktop.
Arrow Custom Authentication Scheme - API Management
var Arrow = require("arrow");
var Model = Arrow.Model.reduce("appc.salesforce/Account","Account",{
"fields": {
"Name": {
"type": "string",
"description": "Account Name",
"readonly": false,
"maxlength": 255,
"required": true,
"optional": false,
"writeonly": false
},
"Type": {
"type": "string",
"description": "Account Type",
"readonly": false,
"maxlength": 40,
"required": false,
"optional": true,
"writeonly": false
},
"Website": {
"type": "string",
"description": "Website",
"readonly": false,
"maxlength": 255,
"required": false,
"optional": true,
"writeonly": false
}
},
"actions": [
"create",
"read",
"update",
"delete",
"deleteAll"
],
"singular": "Account",
"plural": "Accounts"
});
module.exports = Model;
var Arrow = require("arrow");
var keygen = require("keygenerator");
var Model = Arrow.createModel("apiuser",{
"fields": {
"username": {
"type": "String"
},
"key": {
"type": "String",
"set":function(val,key,model){
return keygen._();
}
},
"enabled": {
"type": "Boolean",
"default": true,
},
"count": {
"type": "Number",
"default": 0,
}
},
"before": "validateapiuser",
"connector": "appc.arrowdb",
"actions": [
"create",
"read",
"update",
"delete",
"deleteAll"
],
"singular": "apiuser",
"plural": "apiusers"
});
module.exports = Model;
var Arrow = require("arrow");
var Model = Arrow.createModel("database",{
"fields": {
"fname": {
"type": "String"
},
"lname": {
"type": "String"
},
"title": {
"type": "String"
}
},
"connector": "appc.arrowdb",
"actions": [
"create",
"read",
"update",
"delete",
"deleteAll"
],
// "before": "checkheaders",
"singular": "database",
"plural": "databases"
});
module.exports = Model;
/**
* this is your configuration file defaults.
*
* You can create additional configuration files to that the server will load based on your
* environment. For example, if you want to have specific settings for production which are different
* than your local development environment, you can create a production.js and a local.js. Any changes
* in those files will overwrite changes to this file (a object merge is performed). By default, your
* local.js file will not be commited to git or the registry.
*
* This is a JavaScript file (instead of JSON) so you can also perform logic in this file if needed.
*/
module.exports = {
// these are your generated API keys. They were generated uniquely when you created this project.
// DO NOT SHARE these keys with other projects and be careful with these keys since they control
// access to your API using the default configuration. if you don't want two different keys for
// production and test (not recommended), use the key 'apikey'. To simulate running in production,
// set the environment variable NODE_ENV to production before running such as:
//
// NODE_ENV=production appc run
//
// production key, this is the key that will be required when you are running in production
apikey_production: 'OEp0jzsqpke8NUCT0uoKSk8HQRn27DbP',
// development key, this is the key that will be required when you are testing non-production (such as locally)
apikey_development: '8u6FTQCBWoCifXRXm9PTMkxznosVqvZd',
// by default the authentication strategy is 'basic' which will use HTTP Basic Authorization where the
// usename is the key and the password is blank. the other option is 'apikey' where the value of the
// APIKey header is the value of the key. you can also set this to 'plugin' and define the key 'APIKeyAuthPlugin'
// which points to a file or a module that implements the authentication strategy
// APIKeyAuthType: 'basic',
APIKeyAuthType: 'plugin',
APIKeyAuthPlugin: './lib/plugin.js',
adminsecret: 'adminsecret',
// logging configuration
logging: {
// location of the logs if enabled
logdir: './logs',
// turn on transaction logs
transactionLogEnabled: true
},
// prefix to use for apis
apiPrefix: '/api',
// control the settings for the admin website
admin: {
// control whether the admin website is available
enabled: true,
// the prefix to the admin website
prefix: '/arrow',
// the prefix for the public apidocs website
apiDocPrefix: '/apidoc',
// if you set disableAuth, in production only your API docs will show up
disableAuth: false,
// if you set disableAPIDoc, you APIs docs will not show up (regardless of disableAuth)
disableAPIDoc: false,
// set to true to allow the admin website to be accessed in production. however, you will still need a
// login unless disableAuth is false. if you set this to false, the admin website will not be enabled
// when in production (still respects enabled above)
enableAdminInProduction: true,
// set the email addresses you want to enable while in production (assuming enableAdminInProduction=true)
validEmails: ["[email protected]"],
// set the organization ids you want to enable while in production (assuming enableAdminInProduction=true)
validOrgs: [100000142]
},
// you can generally leave this as-is since it is generated for each new project you created.
session: {
encryptionAlgorithm: 'aes256',
encryptionKey: '8sYsej4sLe9hFmeo24rfwOTgXkdOjp5wbzT60ivof1k=',
signatureAlgorithm: 'sha512-drop256',
signatureKey: 'NXbG67rJOv6v2LmA8BmCmWICNPrkrT/mk8K+zO6TgCdeKmB1Cm2o4C7HTF5lbyPPwN1c1tTdS2vek4/6m81QXA==',
secret: 'D9BBBhcJ62UvRt4c+ijAwFwlcx6Ykilj', // should be a large unguessable string
duration: 86400000, // how long the session will stay valid in ms
activeDuration: 300000 // if expiresIn < activeDuration, the session will be extended by activeDuration milliseconds
},
// your connector configuration goes here
connectors: {
}
};
var Arrow = require('arrow');
function Plugin(server) {
console.log("Plugin()");
this.config = server.config;
}
Plugin.prototype.matchURL = function(request) {
console.log("matchURL(), request.url = "+request.url);
// Validate all apis
return true;
};
Plugin.prototype.validateRequest = function(request, response, callback) {
console.log("validateRequest(), request.url = "+request.url);
//admins have access to all APIs
if (request.headers['admin-secret'] && request.headers['admin-secret'] === this.config.adminsecret) {
callback(null, true);
return false;
}
if(request.url.indexOf('/api/apiuser') !== 0) { //don't allow access to the apiuser database
var model = Arrow.getModel("apiuser");
model.query({key: request.headers['x-api-key'] || "key not found"}, function(err, collection){
if(err) {
callback(null, false);
} else {
if(collection.length>0){
model.findOne(collection[0].id, function(err, data){
if(err) {
callback(errorMsg_serverError, false);
} else {
if(data.enabled) {
data.count++;
data.update();
callback(null, true);
} else {
callback(null, false);
}
}
});
} else {
callback(null, false);
}
}
});
} else {
callback(null, false);
}
};
// Add the admin-secret header for internal requests
Plugin.prototype.applyCredentialsForTest = function(options) {
options.headers['admin-secret'] = this.config.adminsecret;
};
// Do not process the response
Plugin.prototype.applyResponseForTest = function(response, body) {
return body;
};
module.exports = Plugin;
var Arrow = require('arrow');
var PreBlock = Arrow.Block.extend({
name: 'validateapiuser',
description: 'validate api user',
action: function (req, resp, next) {
if((req.method==="POST" || req.method==="PUT")) {
// console.log("req.params = "+JSON.stringify(req.params));
var model = Arrow.getModel("apiuser");
model.query({username: req.params.username}, function(err, data){
if(err) {
// console.log('error getting apisuer database, err = '+err);
resp.response.status(500); //workaround - https://jira.appcelerator.org/browse/API-852
resp.send({"error": "cannot access user api database"});
next(false);
} else {
// console.log("data retreived from apiuser length = "+data.length);
if(data.length > 0) {
resp.response.status(500); //workaround - https://jira.appcelerator.org/browse/API-852
resp.send({"error": "duplicate username"});
next(false);
} else {
next();
}
}
});
} else {
next();
}
}
});
module.exports = PreBlock;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment