webapi-eca/coffee/persistence.coffee

833 lines
24 KiB
CoffeeScript
Raw Normal View History

###
Persistence
============
2013-11-26 22:24:15 +00:00
> Handles the connection to the database and provides functionalities for
2014-02-10 21:28:10 +00:00
> event pollers, action invokers, rules and the encrypted storing of authentication tokens.
2013-11-26 22:24:15 +00:00
> General functionality as a wrapper for the module holds initialization,
> encryption/decryption, the retrieval of modules and shut down.
>
> The general structure for linked data is that the key is stored in a set.
> By fetching all set entries we can then fetch all elements, which is
> automated in this function.
2014-02-10 21:28:10 +00:00
> For example, modules of the same group, e.g. action invokers are registered in an
2013-11-26 22:24:15 +00:00
> unordered set in the database, from where they can be retrieved again. For example
2014-02-10 21:28:10 +00:00
> a new action invoker has its ID (e.g 'probinder') first registered in the set
> 'action-invokers' and then stored in the db with the key 'action-invoker:' + ID
> (e.g. action-invoker:probinder).
>
###
# **Loads Modules:**
# - External Modules:
# [crypto-js](https://github.com/evanvosberg/crypto-js) and
# [redis](https://github.com/mranney/node_redis)
crypto = require 'crypto-js'
redis = require 'redis'
2013-11-26 22:24:15 +00:00
###
Module call
-----------
Initializes the DB connection with the given `db-port` property in the `args` object.
2013-11-26 22:24:15 +00:00
@param {Object} args
###
2013-11-26 22:24:15 +00:00
exports = module.exports = ( args ) =>
@log = args.logger
2014-02-04 07:35:07 +00:00
@db?.quit()
#TODO we need to have a secure concept here, private keys per user
@crypto_key = "}f6y1y}B{.an$}2c$Yl.$mSnF\\HX149u*y8C:@kmN/520Gt\\v'+KFBnQ!\\r<>5X/xRI`sT<Iw;:DPV;4gy:qf]Zq{\"6sgK{,}^\"!]O;qBM3G?]h_`Psw=b6bVXKXry7*"
@db = redis.createClient args[ 'db-port' ],
'localhost', { connect_timeout: 2000 }
@db.on 'error', ( err ) =>
@log.warn err, 'message from DB'
@ep = new IndexedModules( 'event-poller', @db, @log )
@ai = new IndexedModules( 'action-invoker', @db, @log )
###
2013-11-21 21:07:39 +00:00
Checks whether the db is connected and passes either an error on failure after
ten attempts within five seconds, or nothing on success to the callback(err).
@public isConnected( *cb* )
@param {function} cb
###
2013-11-26 22:24:15 +00:00
exports.isConnected = ( cb ) =>
if @db.connected then cb()
else
numAttempts = 0
2013-11-26 22:24:15 +00:00
fCheckConnection = =>
if @db.connected
@log.info 'DB | Successfully connected to DB!'
cb()
else if numAttempts++ < 10
2014-02-04 07:35:07 +00:00
setTimeout fCheckConnection, 100
else
2014-02-04 07:35:07 +00:00
cb new Error 'Connection to DB failed!'
setTimeout fCheckConnection, 100
2014-02-04 07:35:07 +00:00
###
Abstracts logging for simple action replies from the DB.
@private replyHandler( *action* )
@param {String} action
###
replyHandler = ( action ) =>
( err, reply ) =>
2014-02-04 07:35:07 +00:00
if err
@log.warn err, "during '#{ action }'"
2014-02-04 07:35:07 +00:00
else
@log.info "DB | #{ action }: #{ reply }"
###
Push an event into the event queue.
@public pushEvent( *oEvent* )
@param {Object} oEvent
###
exports.pushEvent = ( oEvent ) =>
if oEvent
@log.info "DB | Event pushed into the queue: '#{ oEvent.eventid }'"
@db.rpush 'event_queue', JSON.stringify( oEvent )
2014-02-04 07:35:07 +00:00
else
@log.warn 'DB | Why would you give me an empty event...'
###
2014-02-10 21:28:10 +00:00
Pop an event from the event queue and pass it to cb(err, obj).
@public popEvent( *cb* )
@param {function} cb
###
2014-02-04 07:35:07 +00:00
exports.popEvent = ( cb ) =>
makeObj = ( pcb ) ->
( err, obj ) ->
pcb err, JSON.parse( obj )
@db.lpop 'event_queue', makeObj( cb )
2014-02-04 07:35:07 +00:00
###
Purge the event queue.
@public purgeEventQueue()
###
exports.purgeEventQueue = () =>
@db.del 'event_queue', replyHandler 'purging event queue'
###
Hashes a string based on SHA-3-512.
@private hash( *plainText* )
@param {String} plainText
###
hash = ( plainText ) =>
if !plainText? then return null
try
2014-02-04 07:35:07 +00:00
( crypto.SHA3 plainText, { outputLength: 512 } ).toString()
catch err
@log.warn err, 'DB | during hashing'
null
###
Encrypts a string using the crypto key from the config file, based on aes-256-cbc.
@private encrypt( *plainText* )
@param {String} plainText
###
2013-11-26 22:24:15 +00:00
encrypt = ( plainText ) =>
if !plainText? then return null
2014-02-10 21:28:10 +00:00
try
crypto.AES.encrypt plainText, @crypto_key
catch err
@log.warn err, 'DB | during encryption'
null
###
Decrypts an encrypted string and hands it back on success or null.
@private decrypt( *crypticText* )
@param {String} crypticText
###
2013-11-26 22:24:15 +00:00
decrypt = ( crypticText ) =>
if !crypticText? then return null;
try
2014-02-10 21:28:10 +00:00
dec = crypto.AES.decrypt crypticText, @crypto_key
dec.toString(crypto.enc.Utf8)
catch err
@log.warn err, 'DB | during decryption'
null
###
2014-02-10 21:28:10 +00:00
Fetches all linked data set keys from a linking set, fetches the single
data objects via the provided function and returns the results to cb(err, obj).
@private getSetRecords( *set, fSingle, cb* )
@param {String} set the set name how it is stored in the DB
2014-02-10 21:28:10 +00:00
@param {function} fSingle a function to retrieve a single data element
per set entry
@param {function} cb the callback(err, obj) function that receives all
the retrieved data or an error
###
2013-11-26 22:24:15 +00:00
getSetRecords = ( set, fSingle, cb ) =>
@log.info "DB | Fetching set records: '#{ set }'"
2014-02-10 21:28:10 +00:00
# Fetch all members of the set
@db.smembers set, ( err, arrReply ) =>
if err
2014-02-10 21:28:10 +00:00
# If an error happens we return it to the callback function
@log.warn err, "DB | fetching '#{ set }'"
2014-02-10 21:28:10 +00:00
cb err
else if arrReply.length == 0
2014-02-10 21:28:10 +00:00
# If the set was empty we return null to the callback
cb()
else
2014-02-10 21:28:10 +00:00
# We need to fetch all the entries from the set and use a semaphore
# since the fetching from the DB will happen asynchronously
semaphore = arrReply.length
objReplies = {}
setTimeout ->
2014-02-10 21:28:10 +00:00
# We use a timeout function to cancel the operation
# in case the DB does not respond
if semaphore > 0
2014-02-10 21:28:10 +00:00
cb new Error "Timeout fetching '#{ set }'"
, 2000
fCallback = ( prop ) =>
2014-02-10 21:28:10 +00:00
# The callback function is required to preprocess the result before
# handing it to the callback. This especially includes decrementing
# the semaphore
( err, data ) =>
--semaphore
if err
@log.warn err, "DB | fetching single element: '#{ prop }'"
else if not data
2014-02-10 21:28:10 +00:00
# There was no data behind the key
@log.warn new Error "Empty key in DB: '#{ prop }'"
else
2014-02-10 21:28:10 +00:00
# We found a valid record and add it to the reply object
2014-02-04 07:35:07 +00:00
objReplies[ prop ] = data
if semaphore == 0
2014-02-10 21:28:10 +00:00
# If all fetch calls returned we finally pass the result
# to the callback
cb null, objReplies
2014-02-10 21:28:10 +00:00
# Since we retrieved an array of keys, we now execute the fSingle function
# on each of them, to retrieve the ata behind the key. Our fCallback function
# is used to preprocess the answer to determine correct execution
2014-02-04 07:35:07 +00:00
fSingle reply, fCallback( reply ) for reply in arrReply
2014-02-10 21:28:10 +00:00
class IndexedModules
constructor: ( @setname, @db, @log ) ->
@log.info "DB | Instantiated indexed modules for '#{ @setname }'"
2014-02-10 21:28:10 +00:00
storeModule: ( mId, data ) =>
@log.info "DB | storeModule(#{ @setname }): #{ mId }"
2014-02-10 21:28:10 +00:00
@db.sadd "#{ @setname }s", mId,
replyHandler "Storing '#{ @setname }' key '#{ mId }'"
@db.set "#{ @setname }:#{ mId }", data,
replyHandler "Storing '#{ @setname }:#{ mId }'"
getModule: ( mId, cb ) =>
@log.info "DB | getModule('#{ @setname }): #{ mId }'"
2014-02-10 21:28:10 +00:00
@db.get "#{ @setname }:#{ mId }", cb
getModuleIds: ( cb ) =>
@log.info "DB | getModuleIds(#{ @setname })"
2014-02-10 21:28:10 +00:00
@db.smembers "#{ @setname }s", cb
getModules: ( cb ) =>
@log.info "DB | getModules(#{ @setname })"
2014-02-10 21:28:10 +00:00
getSetRecords "#{ @setname }s", @getModule, cb
deleteModule: ( mId ) =>
@log.info "DB | deleteModule(#{ @setname }): #{ mId }"
2014-02-10 21:28:10 +00:00
@db.srem "#{ @setname }s", mId,
replyHandler "Deleting '#{ @setname }' key '#{ mId }'"
@db.del "#{ @setname }:#{ mId }",
replyHandler "Deleting '#{ @setname }:#{ mId }'"
storeParameters: ( mId, userId, data ) =>
@log.info "DB | storeParameters(#{ @setname }): '#{ mId }:#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.sadd "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Storing '#{ @setname }' module parameters key '#{ mId }'"
@db.set "#{ @setname }-params:#{ mId }:#{ userId }", encrypt(data),
replyHandler "Storing '#{ @setname }' module parameters '#{ mId }:#{ userId }'"
getParameters: ( mId, userId, cb ) =>
@log.info "DB | getParameters(#{ @setname }): '#{ mId }:#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.get "#{ @setname }-params:#{ mId }:#{ userId }", ( err, data ) ->
cb err, decrypt data
getParametersIds: ( cb ) =>
@log.info "DB | getParametersIds(#{ @setname })"
2014-02-10 21:28:10 +00:00
@db.smembers "#{ @setname }-params", cb
deleteParameters: ( mId, userId ) =>
@log.info "DB | deleteParameters(#{ @setname }): '#{ mId }:#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.srem "#{ @setname }-params", "#{ mId }:#{ userId }",
replyHandler "Deleting '#{ @setname }-params' key '#{ mId }:#{ userId }'"
@db.del "#{ @setname }-params:#{ mId }:#{ userId }",
replyHandler "Deleting '#{ @setname }-params:#{ mId }:#{ userId }'"
###
2014-02-10 21:28:10 +00:00
## Action Invokers
###
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Store a string representation of an action invoker in the DB.
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public storeActionInvoker ( *aiId, data* )
@param {String} aiId
2013-11-21 21:07:39 +00:00
@param {String} data
###
2014-02-10 21:28:10 +00:00
exports.storeActionInvoker = ( aiId, data ) =>
@ai.storeModule( aiId, data )
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Query the DB for an action invoker and pass it to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getActionInvoker( *aiId, cb* )
@param {String} aiId
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getActionInvoker = ( aiId, cb ) =>
@ai.getModule aiId, cb
###
2014-02-10 21:28:10 +00:00
Fetch all action invoker IDs and hand them to cb(err, obj).
2014-02-10 21:28:10 +00:00
@public getActionInvokerIds( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getActionInvokerIds = ( cb ) =>
@ai.getModuleIds cb
###
2014-02-10 21:28:10 +00:00
Fetch all action invokers and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getActionInvokers( *cb* )
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getActionInvokers = ( cb ) =>
@ai.getModules cb
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Fetch all action invokers and hand them to cb(err, obj).
2014-02-10 21:28:10 +00:00
@public getActionInvokers( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.deleteActionInvoker = ( aiId ) =>
@ai.deleteModule aiId
###
2014-02-10 21:28:10 +00:00
Store user-specific action invoker parameters .
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public storeActionParams( *userId, aiId, data* )
2013-11-21 21:07:39 +00:00
@param {String} userId
2014-02-10 21:28:10 +00:00
@param {String} aiId
2013-11-21 21:07:39 +00:00
@param {String} data
###
2014-02-10 21:28:10 +00:00
exports.storeActionParams = ( aiId, userId, data ) =>
@ai.storeParameters aiId, userId, data
2013-11-21 21:07:39 +00:00
###
2014-02-04 07:35:07 +00:00
Query the DB for user-specific action module parameters,
2014-02-10 21:28:10 +00:00
and pass it to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getActionParams( *userId, aiId, cb* )
2013-11-21 21:07:39 +00:00
@param {String} userId
2014-02-10 21:28:10 +00:00
@param {String} aiId
@param {function} cb
###
exports.getActionParams = ( aiId, userId, cb ) =>
@ai.getParameters aiId, userId, cb
###
Fetch all action params IDs and hand them to cb(err, obj).
@public getActionParamsIds( *cb* )
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getActionParamsIds = ( cb ) =>
@ai.getParametersIds cb
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
###
Fetch all action modules and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public deleteActionParams( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.deleteActionParams = ( aiId, userId ) =>
@ai.deleteParameters aiId, userId
###
## Event Pollers
2013-11-21 21:07:39 +00:00
###
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Store a string representation of an event poller in the DB.
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public storeEventPoller ( *epId, data* )
@param {String} epId
2013-11-21 21:07:39 +00:00
@param {String} data
###
2014-02-10 21:28:10 +00:00
exports.storeEventPoller = ( epId, data ) =>
@ep.storeModule( epId, data )
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Query the DB for an event poller and pass it to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getEventPoller( *epId, cb* )
@param {String} epId
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getEventPoller = ( epId, cb ) =>
@ep.getModule epId, cb
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Fetch all event poller IDs and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getEventPollerIds( *cb* )
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getEventPollerIds = ( cb ) =>
@ep.getModuleIds cb
###
Fetch all event pollers and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getEventPollers( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getEventPollers = ( cb ) =>
@ep.getModules cb
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
###
Fetch all event pollers and hand them to cb(err, obj).
@public getEventPollers( *cb* )
@param {function} cb
###
exports.deleteEventPoller = ( epId ) =>
@ep.deleteModule epId
###
Store user-specific event poller parameters .
@public storeEventParams( *userId, epId, data* )
2013-11-26 22:24:15 +00:00
@param {String} userId
2014-02-10 21:28:10 +00:00
@param {String} epId
@param {String} data
###
exports.storeEventParams = ( epId, userId, data ) =>
@ep.storeParameters epId, userId, data
###
2014-02-10 21:28:10 +00:00
Query the DB for user-specific event module parameters,
and pass it to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getEventParams( *userId, epId, cb* )
2013-11-26 22:24:15 +00:00
@param {String} userId
2014-02-10 21:28:10 +00:00
@param {String} epId
2013-11-21 21:07:39 +00:00
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getEventParams = ( epId, userId, cb ) =>
@ep.getParameters epId, userId, cb
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
###
Fetch all event params IDs and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public getEventParamsIds( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.getEventParamsIds = ( cb ) =>
@ep.getParametersIds cb
###
2014-02-10 21:28:10 +00:00
Fetch all event modules and hand them to cb(err, obj).
2013-11-21 21:07:39 +00:00
2014-02-10 21:28:10 +00:00
@public deleteEventParams( *cb* )
@param {function} cb
###
2014-02-10 21:28:10 +00:00
exports.deleteEventParams = ( epId, userId ) =>
@ep.deleteParameters epId, userId
###
2014-02-10 21:28:10 +00:00
## Rules
###
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Query the DB for a rule and pass it to cb(err, obj).
2013-11-21 21:07:39 +00:00
@public getRule( *ruleId, cb* )
@param {String} ruleId
2013-11-21 21:07:39 +00:00
@param {function} cb
###
exports.getRule = ( ruleId, cb ) =>
@log.info "DB | getRule: '#{ ruleId }'"
@db.get "rule:#{ ruleId }", cb
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Fetch all rules and pass them to cb(err, obj).
2013-11-21 21:07:39 +00:00
@public getRules( *cb* )
@param {function} cb
###
exports.getRules = ( cb ) =>
@log.info 'DB | Fetching all Rules'
2013-11-21 21:07:39 +00:00
getSetRecords 'rules', exports.getRule, cb
2014-02-10 21:28:10 +00:00
###
Fetch all rule IDs and hand it to cb(err, obj).
@public getRuleIds( *cb* )
@param {function} cb
###
exports.getRuleIds = ( cb ) =>
@log.info 'DB | Fetching all Rule IDs'
2014-02-10 21:28:10 +00:00
@db.smembers 'rules', cb
###
Store a string representation of a rule in the DB.
@public storeRule( *ruleId, data* )
@param {String} ruleId
@param {String} data
###
exports.storeRule = ( ruleId, data ) =>
@log.info "DB | storeRule: '#{ ruleId }'"
2014-02-10 21:28:10 +00:00
@db.sadd 'rules', "#{ ruleId }",
replyHandler "storing rule key '#{ ruleId }'"
@db.set "rule:#{ ruleId }", data,
replyHandler "storing rule '#{ ruleId }'"
###
Delete a string representation of a rule.
@public deleteRule( *ruleId, userId* )
@param {String} ruleId
@param {String} userId
###
exports.deleteRule = ( ruleId ) =>
@log.info "DB | deleteRule: '#{ ruleId }'"
2014-02-10 21:28:10 +00:00
@db.srem "rules", ruleId, replyHandler "Deleting rule key '#{ ruleId }'"
@db.del "rule:#{ ruleId }", replyHandler "Deleting rule '#{ ruleId }'"
# We also need to delete all references in linked and active users
@db.smembers "rule:#{ ruleId }:users", ( err, obj ) =>
delLinkedUserRule = ( userId ) =>
@db.srem "user:#{ userId }:rules", ruleId,
replyHandler "Deleting rule key '#{ ruleId }' in linked user '#{ userId }'"
2014-02-10 21:28:10 +00:00
delLinkedUserRule( id ) for id in obj
@db.del "rule:#{ ruleId }:users", replyHandler "Deleting rule '#{ ruleId }' users"
@db.smembers "rule:#{ ruleId }:active-users", ( err, obj ) =>
delActiveUserRule = ( userId ) =>
@db.srem "user:#{ userId }:active-rules", ruleId,
replyHandler "Deleting rule key '#{ ruleId }' in active user '#{ userId }'"
delActiveUserRule( id ) for id in obj
@db.del "rule:#{ ruleId }:active-users",
replyHandler "Deleting rule '#{ ruleId }' active users"
###
Associate a rule to a user.
@public linkRule( *ruleId, userId* )
@param {String} ruleId
@param {String} userId
###
exports.linkRule = ( ruleId, userId ) =>
@log.info "DB | linkRule: '#{ ruleId }' for user '#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.sadd "rule:#{ ruleId }:users", userId,
replyHandler "storing user '#{ userId }' for rule key '#{ ruleId }'"
@db.sadd "user:#{ userId }:rules", ruleId,
replyHandler "storing rule key '#{ ruleId }' for user '#{ userId }'"
###
Get rules linked to a user and hand it to cb(err, obj).
@public getUserLinkRule( *userId, cb* )
@param {String} userId
@param {function} cb
###
exports.getUserLinkedRules = ( userId, cb ) =>
@log.info "DB | getUserLinkedRules: for user '#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.smembers "user:#{ userId }:rules", cb
###
Get users linked to a rule and hand it to cb(err, obj).
@public getRuleLinkedUsers( *ruleId, cb* )
@param {String} ruleId
@param {function} cb
###
exports.getRuleLinkedUsers = ( ruleId, cb ) =>
@log.info "DB | getRuleLinkedUsers: for rule '#{ ruleId }'"
2014-02-10 21:28:10 +00:00
@db.smembers "rule:#{ ruleId }:users", cb
###
Delete an association of a rule to a user.
@public unlinkRule( *ruleId, userId* )
@param {String} ruleId
@param {String} userId
###
exports.unlinkRule = ( ruleId, userId ) =>
@log.info "DB | unlinkRule: '#{ ruleId }:#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.srem "rule:#{ ruleId }:users", userId,
replyHandler "removing user '#{ userId }' for rule key '#{ ruleId }'"
@db.srem "user:#{ userId }:rules", ruleId,
replyHandler "removing rule key '#{ ruleId }' for user '#{ userId }'"
###
Activate a rule.
@public activateRule( *ruleId, userId* )
@param {String} ruleId
@param {String} userId
###
exports.activateRule = ( ruleId, userId ) =>
@log.info "DB | activateRule: '#{ ruleId }' for '#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.sadd "rule:#{ ruleId }:active-users", userId,
replyHandler "storing activated user '#{ userId }' in rule '#{ ruleId }'"
@db.sadd "user:#{ userId }:active-rules", ruleId,
replyHandler "storing activated rule '#{ ruleId }' in user '#{ userId }'"
###
Get rules activated for a user and hand it to cb(err, obj).
@public getUserLinkRule( *userId, cb* )
@param {String} userId
@param {function} cb
###
exports.getUserActivatedRules = ( userId, cb ) =>
@log.info "DB | getUserActivatedRules: for user '#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.smembers "user:#{ userId }:active-rules", cb
###
Get users activated for a rule and hand it to cb(err, obj).
@public getRuleActivatedUsers ( *ruleId, cb* )
@param {String} ruleId
@param {function} cb
###
exports.getRuleActivatedUsers = ( ruleId, cb ) =>
@log.info "DB | getRuleActivatedUsers: for rule '#{ ruleId }'"
2014-02-10 21:28:10 +00:00
@db.smembers "rule:#{ ruleId }:active-users", cb
###
Deactivate a rule.
@public deactivateRule( *ruleId, userId* )
@param {String} ruleId
@param {String} userId
###
exports.deactivateRule = ( ruleId, userId ) =>
@log.info "DB | deactivateRule: '#{ ruleId }' for '#{ userId }'"
2014-02-10 21:28:10 +00:00
@db.srem "rule:#{ ruleId }:active-users", userId,
replyHandler "removing activated user '#{ userId }' in rule '#{ ruleId }'"
@db.srem "user:#{ userId }:active-rules", ruleId,
replyHandler "removing activated rule '#{ ruleId }' in user '#{ userId }'"
###
Fetch all active ruleIds and pass them to cb(err, obj).
@public getAllActivatedRuleIds( *cb* )
@param {function} cb
###
exports.getAllActivatedRuleIdsPerUser = ( cb ) =>
@log.info "DB | Fetching all active rules"
2014-02-10 21:28:10 +00:00
@db.smembers 'users', ( err, obj ) =>
result = {}
if obj.length is 0
cb null, result
else
semaphore = obj.length
fFetchActiveUserRules = ( userId ) =>
@db.smembers "user:#{ user }:active-rules", ( err, obj ) =>
if obj.length > 0
2014-02-10 21:28:10 +00:00
result[userId] = obj
if --semaphore is 0
cb null, result
fFetchActiveUserRules(user) for user in obj
###
## Users
###
2014-02-10 21:28:10 +00:00
###
2013-11-21 21:07:39 +00:00
Store a user object (needs to be a flat structure).
The password should be hashed before it is passed to this function.
2013-11-21 21:07:39 +00:00
@public storeUser( *objUser* )
@param {Object} objUser
###
2013-11-26 22:24:15 +00:00
exports.storeUser = ( objUser ) =>
#TODO Only store user if not already existing, or at least only then add a private key
#for his encryption. we would want to have one private key per user, right?
@log.info "DB | storeUser: '#{ objUser.username }'"
2013-11-21 21:07:39 +00:00
if objUser and objUser.username and objUser.password
2014-02-10 21:28:10 +00:00
@db.sadd 'users', objUser.username,
replyHandler "storing user key '#{ objUser.username }'"
objUser.password = objUser.password
2014-02-10 21:28:10 +00:00
@db.hmset "user:#{ objUser.username }", objUser,
replyHandler "storing user properties '#{ objUser.username }'"
2013-11-21 21:07:39 +00:00
else
@log.warn new Error 'DB | username or password was missing'
###
Fetch all user IDs and pass them to cb(err, obj).
@public getUserIds( *cb* )
@param {function} cb
###
exports.getUserIds = ( cb ) =>
@log.info "DB | getUserIds"
@db.smembers "users", cb
###
Fetch a user by id and pass it to cb(err, obj).
@public getUser( *userId, cb* )
@param {String} userId
@param {function} cb
###
exports.getUser = ( userId, cb ) =>
@log.info "DB | getUser: '#{ userId }'"
@db.hgetall "user:#{ userId }", cb
###
Deletes a user and all his associated linked and active rules.
@public deleteUser( *userId* )
@param {String} userId
###
exports.deleteUser = ( userId ) =>
@log.info "DB | deleteUser: '#{ userId }'"
@db.srem "users", userId, replyHandler "Deleting user key '#{ userId }'"
@db.del "user:#{ userId }", replyHandler "Deleting user '#{ userId }'"
# We also need to delete all linked rules
@db.smembers "user:#{ userId }:rules", ( err, obj ) =>
delLinkedRuleUser = ( ruleId ) =>
@db.srem "rule:#{ ruleId }:users", userId,
replyHandler "Deleting user key '#{ userId }' in linked rule '#{ ruleId }'"
delLinkedRuleUser( id ) for id in obj
@db.del "user:#{ userId }:rules",
replyHandler "Deleting user '#{ userId }' rules"
# We also need to delete all active rules
@db.smembers "user:#{ userId }:active-rules", ( err, obj ) =>
delActivatedRuleUser = ( ruleId ) =>
@db.srem "rule:#{ ruleId }:active-users", userId,
replyHandler "Deleting user key '#{ userId }' in active rule '#{ ruleId }'"
delActivatedRuleUser( id ) for id in obj
@db.del "user:#{ userId }:active-rules",
replyHandler "Deleting user '#{ userId }' rules"
# We also need to delete all associated roles
@db.smembers "user:#{ userId }:roles", ( err, obj ) =>
delRoleUser = ( roleId ) =>
@db.srem "role:#{ roleId }:users", userId,
replyHandler "Deleting user key '#{ userId }' in role '#{ roleId }'"
delRoleUser( id ) for id in obj
@db.del "user:#{ userId }:roles",
replyHandler "Deleting user '#{ userId }' roles"
###
Checks the credentials and on success returns the user object to the
callback(err, obj) function. The password has to be hashed (SHA-3-512)
beforehand by the instance closest to the user that enters the password,
because we only store hashes of passwords for security6 reasons.
@public loginUser( *userId, password, cb* )
@param {String} userId
@param {String} password
@param {function} cb
###
#TODO verify and test whole function
exports.loginUser = ( userId, password, cb ) =>
@log.info "DB | User '#{ userId }' tries to log in"
fCheck = ( pw ) =>
( err, obj ) =>
if err
cb err, null
else if obj and obj.password
if pw == obj.password
@log.info "DB | User '#{ obj.username }' logged in!"
cb null, obj
else
cb (new Error 'Wrong credentials!'), null
else
cb (new Error 'User not found!'), null
@db.hgetall "user:#{ userId }", fCheck password
#TODO implement functions required for user sessions?
###
## User Roles
###
###
2013-11-21 21:07:39 +00:00
Associate a role with a user.
@public storeUserRole( *userId, role* )
@param {String} userId
2013-11-21 21:07:39 +00:00
@param {String} role
###
exports.storeUserRole = ( userId, role ) =>
@log.info "DB | storeUserRole: '#{ userId }:#{ role }'"
2014-02-10 21:28:10 +00:00
@db.sadd 'roles', role, replyHandler "adding role '#{ role }' to role index set"
@db.sadd "user:#{ userId }:roles", role,
replyHandler "adding role '#{ role }' to user '#{ userId }'"
@db.sadd "role:#{ role }:users", userId,
replyHandler "adding user '#{ userId }' to role '#{ role }'"
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Fetch all roles of a user and pass them to cb(err, obj).
@public getUserRoles( *userId* )
@param {String} userId
@param {function} cb
###
exports.getUserRoles = ( userId, cb ) =>
@log.info "DB | getUserRoles: '#{ userId }'"
@db.smembers "user:#{ userId }:roles", cb
2013-11-21 21:07:39 +00:00
###
2014-02-10 21:28:10 +00:00
Fetch all users of a role and pass them to cb(err, obj).
2013-11-21 21:07:39 +00:00
@public getUserRoles( *role* )
@param {String} role
@param {function} cb
###
exports.getRoleUsers = ( role, cb ) =>
@log.info "DB | getRoleUsers: '#{ role }'"
@db.smembers "role:#{ role }:users", cb
###
Remove a role from a user.
2013-11-21 21:07:39 +00:00
@public removeRoleFromUser( *role, userId* )
@param {String} role
@param {String} userId
###
exports.removeUserRole = ( userId, role ) =>
@log.info "DB | removeRoleFromUser: role '#{ role }', user '#{ userId }'"
@db.srem "user:#{ userId }:roles", role,
replyHandler "Removing role '#{ role }' from user '#{ userId }'"
@db.srem "role:#{ role }:users", userId,
replyHandler "Removing user '#{ userId }' from role '#{ role }'"
2013-11-21 21:07:39 +00:00
###
2013-11-21 21:07:39 +00:00
Shuts down the db link.
@public shutDown()
###
exports.shutDown = () => @db?.quit()