mirror of
https://github.com/Hopiu/webapi-eca.git
synced 2026-03-16 22:10:31 +00:00
915 lines
29 KiB
CoffeeScript
915 lines
29 KiB
CoffeeScript
###
|
|
|
|
Persistence
|
|
============
|
|
> Handles the connection to the database and provides functionalities for event pollers,
|
|
> action invokers, rules and the (hopefully encrypted) storing of user-specific parameters
|
|
> per module.
|
|
> General functionality as a wrapper for the module holds initialization,
|
|
> 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.
|
|
> For example, modules of the same group, e.g. action invokers are registered in an
|
|
> unordered set in the database, from where they can be retrieved again. For example
|
|
> 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:
|
|
# [redis](https://github.com/mranney/node_redis)
|
|
redis = require 'redis'
|
|
|
|
###
|
|
Module call
|
|
-----------
|
|
Initializes the DB connection with the given `db-port` property in the `args` object.
|
|
|
|
@param {Object} args
|
|
###
|
|
exports = module.exports = ( args ) =>
|
|
if not @db
|
|
#TODO we need to have a secure concept here, private keys per user
|
|
#FIXME get rid of crpto
|
|
if not args[ 'db-port' ]
|
|
args[ 'db-port' ] = 6379
|
|
@log = args.logger
|
|
exports.eventPollers = new IndexedModules 'event-poller', @log
|
|
exports.actionInvokers = new IndexedModules 'action-invoker', @log
|
|
exports.initPort args[ 'db-port' ]
|
|
|
|
exports.getLogger = () =>
|
|
@log
|
|
|
|
exports.initPort = ( port ) =>
|
|
@connRefused = false
|
|
@db?.quit()
|
|
@db = redis.createClient port,
|
|
'localhost', { connect_timeout: 2000 }
|
|
# Eventually we try to connect to the wrong port, redis will emit an error that we
|
|
# need to catch and take into account when answering the isConnected function call
|
|
@db.on 'error', ( err ) =>
|
|
if err.message.indexOf( 'ECONNREFUSED' ) > -1
|
|
@connRefused = true
|
|
@log.warn 'DB | Wrong port?'
|
|
else
|
|
@log.error err
|
|
exports.eventPollers.setDB @db
|
|
exports.actionInvokers.setDB @db
|
|
|
|
exports.selectDatabase = ( id ) =>
|
|
@db.select id
|
|
|
|
###
|
|
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
|
|
###
|
|
exports.isConnected = ( cb ) =>
|
|
if not @db
|
|
cb new Error 'DB | DB initialization did not occur or failed miserably!'
|
|
else
|
|
if @db.connected
|
|
cb()
|
|
else
|
|
numAttempts = 0
|
|
fCheckConnection = =>
|
|
if @connRefused
|
|
@db?.quit()
|
|
cb new Error 'DB | Connection refused! Wrong port?'
|
|
else
|
|
if @db.connected
|
|
@log.info 'DB | Successfully connected to DB!'
|
|
cb()
|
|
else if numAttempts++ < 10
|
|
setTimeout fCheckConnection, 100
|
|
else
|
|
cb new Error 'DB | Connection to DB failed!'
|
|
setTimeout fCheckConnection, 100
|
|
|
|
|
|
###
|
|
Abstracts logging for simple action replies from the DB.
|
|
|
|
@private replyHandler( *action* )
|
|
@param {String} action
|
|
###
|
|
replyHandler = ( action ) =>
|
|
( err, reply ) =>
|
|
if err
|
|
@log.warn err, "during '#{ action }'"
|
|
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.eventname }'"
|
|
@db.rpush 'event_queue', JSON.stringify oEvent
|
|
else
|
|
@log.warn 'DB | Why would you give me an empty event...'
|
|
|
|
|
|
###
|
|
Pop an event from the event queue and pass it to cb(err, obj).
|
|
|
|
@public popEvent( *cb* )
|
|
@param {function} cb
|
|
###
|
|
exports.popEvent = ( cb ) =>
|
|
makeObj = ( pcb ) ->
|
|
( err, obj ) ->
|
|
pcb err, JSON.parse obj
|
|
@db.lpop 'event_queue', makeObj cb
|
|
|
|
|
|
###
|
|
Purge the event queue.
|
|
|
|
@public purgeEventQueue()
|
|
###
|
|
exports.purgeEventQueue = () =>
|
|
@db.del 'event_queue', replyHandler 'purging event queue'
|
|
|
|
|
|
###
|
|
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
|
|
@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
|
|
###
|
|
getSetRecords = ( set, fSingle, cb ) =>
|
|
@log.info "DB | Fetching set records: '#{ set }'"
|
|
# Fetch all members of the set
|
|
@db.smembers set, ( err, arrReply ) =>
|
|
if err
|
|
# If an error happens we return it to the callback function
|
|
@log.warn err, "DB | fetching '#{ set }'"
|
|
cb err
|
|
else if arrReply.length == 0
|
|
# If the set was empty we return null to the callback
|
|
cb()
|
|
else
|
|
# 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 ->
|
|
# We use a timeout function to cancel the operation
|
|
# in case the DB does not respond
|
|
if semaphore > 0
|
|
cb new Error "Timeout fetching '#{ set }'"
|
|
, 2000
|
|
fCallback = ( prop ) =>
|
|
# 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
|
|
# There was no data behind the key
|
|
@log.warn new Error "Empty key in DB: '#{ prop }'"
|
|
else
|
|
# We found a valid record and add it to the reply object
|
|
objReplies[ prop ] = data
|
|
if semaphore == 0
|
|
# If all fetch calls returned we finally pass the result
|
|
# to the callback
|
|
cb null, objReplies
|
|
# 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
|
|
fSingle reply, fCallback reply for reply in arrReply
|
|
|
|
|
|
class IndexedModules
|
|
constructor: ( @setname, @log ) ->
|
|
@log.info "DB | (IdxedMods) Instantiated indexed modules for '#{ @setname }'"
|
|
|
|
setDB: ( @db ) ->
|
|
@log.info "DB | (IdxedMods) Registered new DB connection for '#{ @setname }'"
|
|
|
|
###
|
|
Stores a module and links it to the user.
|
|
|
|
@private storeModule( *userId, oModule* )
|
|
@param {String} userId
|
|
@param {object} oModule
|
|
###
|
|
storeModule: ( userId, oModule ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.storeModule( #{ userId }, oModule )"
|
|
@db.sadd "user:#{ userId }:#{ @setname }s", oModule.id,
|
|
replyHandler "sadd 'user:#{ userId }:#{ @setname }s' -> #{ oModule.id }"
|
|
@db.hmset "user:#{ userId }:#{ @setname }:#{ oModule.id }", oModule,
|
|
replyHandler "hmset 'user:#{ userId }:#{ @setname }:#{ oModule.id }' -> [oModule]"
|
|
# @linkModule oModule.id, userId
|
|
|
|
# #TODO add testing
|
|
# linkModule: ( mId, userId ) =>
|
|
# @log.info "DB | (IdxedMods) #{ @setname }.linkModule( #{ mId }, #{ userId } )"
|
|
# @db.sadd "#{ @setname }:#{ mId }:users", userId,
|
|
# replyHandler "sadd '#{ @setname }:#{ mId }:users' -> '#{ userId }'"
|
|
# @db.sadd "user:#{ userId }:#{ @setname }s", mId,
|
|
# replyHandler "sadd 'user:#{ userId }:#{ @setname }s' -> #{ mId }"
|
|
|
|
# #TODO add testing
|
|
# unlinkModule: ( mId, userId ) =>
|
|
# @log.info "DB | (IdxedMods) #{ @setname }.unlinkModule( #{ mId }, #{ userId } )"
|
|
# @db.srem "#{ @setname }:#{ mId }:users", userId,
|
|
# replyHandler "srem '#{ @setname }:#{ mId }:users' -> #{ userId }"
|
|
# @db.srem "user:#{ userId }:#{ @setname }s", mId,
|
|
# replyHandler "srem 'user:#{ userId }:#{ @setname }s' -> #{ mId }"
|
|
|
|
# #TODO add testing
|
|
# publish: ( mId ) =>
|
|
# @log.info "DB | (IdxedMods) #{ @setname }.publish( #{ mId } )"
|
|
# @db.sadd "public-#{ @setname }s", mId,
|
|
# replyHandler "sadd 'public-#{ @setname }s' -> '#{ mId }'"
|
|
|
|
# #TODO add testing
|
|
# unpublish: ( mId ) =>
|
|
# @log.info "DB | (IdxedMods) #{ @setname }.unpublish( #{ mId } )"
|
|
# @db.srem "public-#{ @setname }s", mId,
|
|
# replyHandler "srem 'public-#{ @setname }s' -> '#{ mId }'"
|
|
|
|
getModule: ( userId, mId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getModule( #{ userId }, #{ mId } )"
|
|
@log.info "hgetall user:#{ userId }:#{ @setname }:#{ mId }"
|
|
@db.hgetall "user:#{ userId }:#{ @setname }:#{ mId }", cb
|
|
|
|
getModuleField: ( userId, mId, field, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getModuleField( #{ userId }, #{ mId }, #{ field } )"
|
|
@db.hget "user:#{ userId }:#{ @setname }:#{ mId }", field, cb
|
|
|
|
#TODO add testing
|
|
getAvailableModuleIds: ( userId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getAvailableModuleIds( #{ userId } )"
|
|
@db.sunion "public-#{ @setname }s", "user:#{ userId }:#{ @setname }s", cb
|
|
|
|
getModuleIds: ( userId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getModuleIds()"
|
|
@db.smembers "user:#{ userId }:#{ @setname }s", cb
|
|
|
|
# getModules: ( cb ) =>
|
|
# @log.info "DB | (IdxedMods) #{ @setname }.getModules()"
|
|
# getSetRecords "#{ @setname }s", @getModule, cb
|
|
|
|
deleteModule: ( userId, mId ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.deleteModule( #{ userId }, #{ mId } )"
|
|
@db.srem "user:#{ userId }:#{ @setname }s", mId,
|
|
replyHandler "srem 'user:#{ userId }:#{ @setname }s' -> '#{ mId }'"
|
|
@db.del "user:#{ userId }:#{ @setname }:#{ mId }",
|
|
replyHandler "del 'user:#{ userId }:#{ @setname }:#{ mId }'"
|
|
# @unpublish mId
|
|
# @unlinkModule mId, userId
|
|
@deleteUserParams mId, userId
|
|
exports.getUserLinkedRules userId, ( err, obj ) =>
|
|
for rule in obj
|
|
@getUserArgumentsFunctions userId, rule, mId, ( err, obj ) =>
|
|
@deleteUserArguments userId, rule, mId
|
|
|
|
###
|
|
Stores user params for a module. They are expected to be RSA encrypted with helps of
|
|
the provided cryptico JS library and will only be decrypted right before the module is loaded!
|
|
|
|
@private storeUserParams( *mId, userId, encData* )
|
|
@param {String} mId
|
|
@param {String} userId
|
|
@param {object} encData
|
|
###
|
|
storeUserParams: ( mId, userId, encData ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.storeUserParams( #{ mId }, #{ userId }, encData )"
|
|
@db.sadd "#{ @setname }-params", "#{ mId }:#{ userId }",
|
|
replyHandler "sadd '#{ @setname }-params' -> '#{ mId }:#{ userId }'"
|
|
@db.set "#{ @setname }-params:#{ mId }:#{ userId }", encData,
|
|
replyHandler "set '#{ @setname }-params:#{ mId }:#{ userId }' -> [encData]"
|
|
|
|
getUserParams: ( mId, userId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getUserParams( #{ mId }, #{ userId } )"
|
|
@db.get "#{ @setname }-params:#{ mId }:#{ userId }", cb
|
|
|
|
getUserParamsIds: ( cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getUserParamsIds()"
|
|
@db.smembers "#{ @setname }-params", cb
|
|
|
|
deleteUserParams: ( mId, userId ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.deleteUserParams( #{ mId }, #{ userId } )"
|
|
@db.srem "#{ @setname }-params", "#{ mId }:#{ userId }",
|
|
replyHandler "srem '#{ @setname }-params' -> '#{ mId }:#{ userId }'"
|
|
@db.del "#{ @setname }-params:#{ mId }:#{ userId }",
|
|
replyHandler "del '#{ @setname }-params:#{ mId }:#{ userId }'"
|
|
|
|
###
|
|
Stores user arguments for a function within a module. They are expected to be RSA encrypted with helps of
|
|
the provided cryptico JS library and will only be decrypted right before the module is loaded!
|
|
|
|
@private storeUserArguments( *userId, ruleId, mId, funcId, encData* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
@param {String} mId
|
|
@param {String} funcId
|
|
@param {object} encData
|
|
###
|
|
storeUserArguments: ( userId, ruleId, mId, funcId, encData ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.storeUserArguments( #{ userId }, #{ ruleId }, #{ mId }, #{ funcId }, encData )"
|
|
@db.sadd "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions", funcId,
|
|
replyHandler "sadd '#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions' -> '#{ funcId }'"
|
|
@db.set "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ funcId }", encData,
|
|
replyHandler "set '#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ funcId }' -> [encData]"
|
|
|
|
getUserArgumentsFunctions: ( userId, ruleId, mId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getUserArgumentsFunctions( #{ userId }, #{ ruleId }, #{ mId } )"
|
|
@db.get "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions", cb
|
|
|
|
getAllModuleUserArguments: ( userId, ruleId, mId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getAllModuleUserArguments( #{ userId }, #{ ruleId }, #{ mId } )"
|
|
@db.smembers "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions", ( err, obj ) =>
|
|
sem = obj.length
|
|
oAnswer = {}
|
|
if sem is 0
|
|
cb null, oAnswer
|
|
else
|
|
for func in obj
|
|
fRegisterFunction = ( func ) =>
|
|
( err, obj ) =>
|
|
if obj
|
|
oAnswer[ func ] = obj
|
|
if --sem is 0
|
|
cb null, oAnswer
|
|
@db.get "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ func }", fRegisterFunction func
|
|
|
|
getUserArguments: ( userId, ruleId, mId, funcId, cb ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.getUserArguments( #{ userId }, #{ ruleId }, #{ mId }, #{ funcId } )"
|
|
@db.get "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ funcId }", cb
|
|
|
|
deleteUserArguments: ( userId, ruleId, mId ) =>
|
|
@log.info "DB | (IdxedMods) #{ @setname }.deleteUserArguments( #{ userId }, #{ ruleId }, #{ mId } )"
|
|
@db.smembers "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:functions", ( err, obj ) =>
|
|
for func in obj
|
|
@db.del "#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ func }",
|
|
replyHandler "del '#{ @setname }:#{ userId }:#{ ruleId }:#{ mId }:function:#{ func }'"
|
|
|
|
|
|
###
|
|
## Rules
|
|
###
|
|
|
|
|
|
###
|
|
Stores data for a module in a rule. This is used to allow persistance for moduöes in rules.
|
|
|
|
@public log( *userId, ruleId, moduleId, field, data* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
@param {String} moduleId
|
|
@param {String} field
|
|
@param {String} data
|
|
###
|
|
exports.persistSetVar = ( userId, ruleId, moduleId, field, data ) =>
|
|
@db.hmset "rulepersistence:#{ userId }:#{ ruleId }:#{ moduleId }", field, data,
|
|
replyHandler "hmset 'rulepersistence:#{ userId }:#{ ruleId }:#{ moduleId }' -> #{ field } = [data]"
|
|
|
|
|
|
###
|
|
Gets data for a module in a rule.
|
|
|
|
@public log( *userId, ruleId, moduleId, field, cb* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
@param {String} moduleId
|
|
@param {String} field
|
|
@param {function} cb
|
|
###
|
|
exports.persistGetVar = ( userId, ruleId, moduleId, field, cb ) =>
|
|
@db.hget "rulepersistence:#{ userId }:#{ ruleId }:#{ moduleId }", field, cb
|
|
|
|
|
|
###
|
|
Appends a log entry.
|
|
|
|
@public log( *userId, ruleId, moduleId, message* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
@param {String} message
|
|
###
|
|
exports.appendLog = ( userId, ruleId, moduleId, message ) =>
|
|
@db.append "#{ userId }:#{ ruleId }:log",
|
|
"[UTC|#{ ( new Date() ).toISOString() }] {#{ moduleId }} #{ message }\n"
|
|
|
|
###
|
|
Retrieves a log entry.
|
|
|
|
@public getLog( *userId, ruleId* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
@param {function} cb
|
|
###
|
|
exports.getLog = ( userId, ruleId, cb ) =>
|
|
@db.get "#{ userId }:#{ ruleId }:log", cb
|
|
|
|
###
|
|
Resets a log entry.
|
|
|
|
@public resetLog( *userId, ruleId* )
|
|
@param {String} userId
|
|
@param {String} ruleId
|
|
###
|
|
exports.resetLog = ( userId, ruleId ) =>
|
|
@db.del "#{ userId }:#{ ruleId }:log",
|
|
replyHandler "del '#{ userId }:#{ ruleId }:log'"
|
|
|
|
###
|
|
Query the DB for a rule and pass it to cb(err, obj).
|
|
|
|
@public getRule( *ruleId, cb* )
|
|
@param {String} ruleId
|
|
@param {function} cb
|
|
###
|
|
exports.getRule = ( ruleId, cb ) =>
|
|
@log.info "DB | getRule( '#{ ruleId }' )"
|
|
@db.get "rule:#{ ruleId }", cb
|
|
|
|
###
|
|
Fetch all rules and pass them to cb(err, obj).
|
|
|
|
@public getRules( *cb* )
|
|
@param {function} cb
|
|
###
|
|
exports.getRules = ( cb ) =>
|
|
@log.info "DB | Fetching all Rules: getSetRecords 'rules'"
|
|
getSetRecords 'rules', exports.getRule, cb
|
|
|
|
###
|
|
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: 'rules'"
|
|
@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 }' )"
|
|
@db.sadd 'rules', "#{ ruleId }",
|
|
replyHandler "sadd 'rules' -> '#{ ruleId }'"
|
|
@db.set "rule:#{ ruleId }", data,
|
|
replyHandler "set 'rule:#{ ruleId }' -> [data]"
|
|
|
|
###
|
|
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 }' )"
|
|
@db.srem "rules", ruleId, replyHandler "srem 'rules' -> '#{ ruleId }'"
|
|
@db.del "rule:#{ ruleId }", replyHandler "del 'rule:#{ ruleId }'"
|
|
|
|
# We also need to delete all references in linked and active users
|
|
@db.smembers "rule:#{ ruleId }:users", ( err, obj ) =>
|
|
delLinkedUserRule = ( userId ) =>
|
|
exports.resetLog userId, ruleId
|
|
@db.srem "user:#{ userId }:rules", ruleId,
|
|
replyHandler "srem 'user:#{ userId }:rules' -> '#{ ruleId }'"
|
|
delLinkedUserRule id for id in obj
|
|
@db.del "rule:#{ ruleId }:users", replyHandler "del 'rule:#{ ruleId }:users'"
|
|
|
|
@db.smembers "rule:#{ ruleId }:active-users", ( err, obj ) =>
|
|
delActiveUserRule = ( userId ) =>
|
|
@db.srem "user:#{ userId }:active-rules", ruleId,
|
|
replyHandler "srem 'user:#{ userId }:active-rules' -> '#{ ruleId }'"
|
|
delActiveUserRule id for id in obj
|
|
@db.del "rule:#{ ruleId }:active-users",
|
|
replyHandler "del 'rule:#{ ruleId }:active-users'"
|
|
#TODO remove module links and params and arguments
|
|
|
|
###
|
|
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 }' to user '#{ userId }'"
|
|
@db.sadd "rule:#{ ruleId }:users", userId,
|
|
replyHandler "sadd 'rule:#{ ruleId }:users' -> '#{ userId }'"
|
|
@db.sadd "user:#{ userId }:rules", ruleId,
|
|
replyHandler "sadd 'user:#{ userId }:rules' -> '#{ ruleId }'"
|
|
|
|
###
|
|
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: smembers 'user:#{ userId }:rules'"
|
|
@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: smembers 'rule:#{ ruleId }:users'"
|
|
@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 }'"
|
|
@db.srem "rule:#{ ruleId }:users", userId,
|
|
replyHandler "srem 'rule:#{ ruleId }:users' -> '#{ userId }'"
|
|
@db.srem "user:#{ userId }:rules", ruleId,
|
|
replyHandler "srem 'user:#{ userId }:rules' -> '#{ ruleId }'"
|
|
|
|
###
|
|
Activate a rule.
|
|
|
|
@public activateRule( *ruleId, userId* )
|
|
@param {String} ruleId
|
|
@param {String} userId
|
|
###
|
|
exports.activateRule = ( ruleId, userId ) =>
|
|
@log.info "DB | activateRule: '#{ ruleId }' for '#{ userId }'"
|
|
@db.sadd "rule:#{ ruleId }:active-users", userId,
|
|
replyHandler "sadd 'rule:#{ ruleId }:active-users' -> '#{ userId }'"
|
|
@db.sadd "user:#{ userId }:active-rules", ruleId,
|
|
replyHandler "sadd 'user:#{ userId }:active-rules' -> '#{ ruleId }'"
|
|
|
|
###
|
|
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: smembers 'user:#{ userId }:active-rules'"
|
|
@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: smembers 'rule:#{ ruleId }:active-users'"
|
|
@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 }'"
|
|
@db.srem "rule:#{ ruleId }:active-users", userId,
|
|
replyHandler "srem 'rule:#{ ruleId }:active-users' -> '#{ userId }'"
|
|
@db.srem "user:#{ userId }:active-rules", ruleId,
|
|
replyHandler "srem 'user:#{ userId }:active-rules' '#{ ruleId }'"
|
|
|
|
###
|
|
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"
|
|
@db.smembers 'users', ( err, obj ) =>
|
|
result = {}
|
|
if obj.length is 0
|
|
cb null, result
|
|
else
|
|
semaphore = obj.length
|
|
for user in obj
|
|
fProcessAnswer = ( user ) ->
|
|
( err, obj ) =>
|
|
if obj.length > 0
|
|
result[user] = obj
|
|
if --semaphore is 0
|
|
cb null, result
|
|
@db.smembers "user:#{ user }:active-rules", fProcessAnswer user
|
|
|
|
###
|
|
## Users
|
|
###
|
|
|
|
###
|
|
Store a user object (needs to be a flat structure).
|
|
The password should be hashed before it is passed to this function.
|
|
|
|
@public storeUser( *objUser* )
|
|
@param {Object} objUser
|
|
###
|
|
exports.storeUser = ( objUser ) =>
|
|
@log.info "DB | storeUser: '#{ objUser.username }'"
|
|
if objUser and objUser.username and objUser.password
|
|
@db.sadd 'users', objUser.username,
|
|
replyHandler "sadd 'users' -> '#{ objUser.username }'"
|
|
@db.hmset "user:#{ objUser.username }", objUser,
|
|
replyHandler "hmset 'user:#{ objUser.username }' -> [objUser]"
|
|
@db.hset "user:#{ objUser.username }", "roles", JSON.stringify( objUser.roles ),
|
|
replyHandler "hset 'user:#{ objUser.username }' field 'roles' -> [objUser]"
|
|
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 }", ( err, obj ) =>
|
|
try
|
|
obj.roles = JSON.parse obj.roles
|
|
cb err, obj
|
|
|
|
###
|
|
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 "srem 'users' -> '#{ userId }'"
|
|
@db.del "user:#{ userId }", replyHandler "del '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 "srem 'rule:#{ ruleId }:users' -> '#{ userId }'"
|
|
delLinkedRuleUser id for id in obj
|
|
@db.del "user:#{ userId }:rules",
|
|
replyHandler "del '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 "srem 'rule:#{ ruleId }:active-users' -> '#{ userId }'"
|
|
delActivatedRuleUser id for id in obj
|
|
@db.del "user:#{ userId }:active-rules",
|
|
replyHandler "del user:#{ userId }:active-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 "srem 'role:#{ roleId }:users' -> '#{ userId }'"
|
|
delRoleUser id for id in obj
|
|
@db.del "user:#{ userId }:roles", replyHandler "del 'user:#{ userId }:roles'"
|
|
# TODO we also need to delete this user's modules
|
|
|
|
###
|
|
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
|
|
###
|
|
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 is obj.password
|
|
@log.info "DB | User '#{ obj.username }' logged in!"
|
|
obj.roles = JSON.parse obj.roles
|
|
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
|
|
###
|
|
|
|
###
|
|
Associate a role with a user.
|
|
|
|
@public storeUserRole( *userId, role* )
|
|
@param {String} userId
|
|
@param {String} role
|
|
###
|
|
exports.storeUserRole = ( userId, role ) =>
|
|
@log.info "DB | storeUserRole: '#{ userId }:#{ role }'"
|
|
@db.sadd 'roles', role, replyHandler "sadd '#{ role }' to 'roles'"
|
|
@db.sadd "user:#{ userId }:roles", role,
|
|
replyHandler "sadd 'user:#{ userId }:roles' -> '#{ role }'"
|
|
@db.sadd "role:#{ role }:users", userId,
|
|
replyHandler "sadd 'role:#{ role }:users' -> '#{ userId }'"
|
|
|
|
###
|
|
Associate a role with a user.
|
|
|
|
@public storeUserRole( *userId, role* )
|
|
@param {String} userId
|
|
@param {String} role
|
|
###
|
|
exports.deleteRole = ( role ) =>
|
|
@log.info "DB | deleteRole: '#{ role }'"
|
|
@db.smembers "role:#{ role }:users", ( err, obj ) =>
|
|
delUserRole = ( userId ) =>
|
|
@db.srem "user:#{ userId }:roles", role,
|
|
replyHandler "srem 'user:#{ userId }:roles' -> '#{ role }'"
|
|
delUserRole id for id in obj
|
|
@db.srem "roles", role,
|
|
replyHandler "srem 'roles' -> '#{ role }'"
|
|
|
|
###
|
|
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
|
|
|
|
###
|
|
Fetch all users of a role and pass them to cb(err, obj).
|
|
|
|
@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.
|
|
|
|
@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 "srem 'user:#{ userId }:roles' -> '#{ role }'"
|
|
@db.srem "role:#{ role }:users", userId,
|
|
replyHandler "srem 'role:#{ role }:users' -> '#{ userId }'"
|
|
|
|
|
|
###
|
|
Creates and stores a webhook.
|
|
|
|
@public createWebhook( *username, hookname* )
|
|
@param {String} username
|
|
@param {String} hookname
|
|
###
|
|
exports.createWebhook = ( username, hookid, hookname ) =>
|
|
@db.sadd "webhooks", hookid, replyHandler "sadd 'webhooks' -> '#{ hookid }'"
|
|
@db.sadd "user:#{ username }:webhooks", hookid,
|
|
replyHandler "sadd 'user:#{ username }:webhooks' -> '#{ hookid }'"
|
|
@db.hmset "webhook:#{ hookid }", 'hookname', hookname, 'username', username,
|
|
replyHandler "set webhook:#{ hookid } -> [#{ hookname }, #{ username }]"
|
|
|
|
###
|
|
Returns a webhook name.
|
|
|
|
@public getWebhookName( *hookid* )
|
|
@param {String} hookid
|
|
###
|
|
exports.getWebhookName = ( hookid, cb ) =>
|
|
@db.hget "webhook:#{ hookid }", "hookname", cb
|
|
|
|
###
|
|
Returns all webhook properties.
|
|
|
|
@public getFullWebhookName( *hookid* )
|
|
@param {String} hookid
|
|
###
|
|
exports.getFullWebhook = ( hookid, cb ) =>
|
|
@db.hgetall "webhook:#{ hookid }", cb
|
|
|
|
###
|
|
Returns all the user's webhooks by ID.
|
|
|
|
@public getUserWebhookIDs( *username* )
|
|
@param {String} username
|
|
###
|
|
exports.getUserWebhookIDs = ( username, cb ) =>
|
|
@db.smembers "user:#{ username }:webhooks", cb
|
|
|
|
###
|
|
Gets all the user's webhooks with names.
|
|
|
|
@public getAllUserWebhookNames( *username* )
|
|
@param {String} username
|
|
###
|
|
exports.getAllUserWebhookNames = ( username, cb ) =>
|
|
getSetRecords "user:#{ username }:webhooks", exports.getWebhookName, cb
|
|
|
|
###
|
|
Returns all webhook IDs.
|
|
|
|
@public getAllWebhookIDs()
|
|
###
|
|
exports.getAllWebhookIDs = ( cb ) =>
|
|
@db.smembers "webhooks", cb
|
|
|
|
###
|
|
Returns all webhooks with names.
|
|
|
|
@public getAllWebhooks()
|
|
###
|
|
exports.getAllWebhooks = ( cb ) =>
|
|
getSetRecords "webhooks", exports.getFullWebhook, cb
|
|
|
|
###
|
|
Delete a webhook.
|
|
|
|
@public deleteWebhook( *username, hookid* )
|
|
@param {String} username
|
|
@param {String} hookid
|
|
###
|
|
exports.deleteWebhook = ( username, hookid ) =>
|
|
@db.srem "webhooks", hookid, replyHandler "srem 'webhooks' -> '#{ hookid }'"
|
|
@db.srem "user:#{ username }:webhooks", hookid,
|
|
replyHandler "srem 'user:#{ username }:webhooks' -> '#{ hookid }'"
|
|
@db.del "webhook:#{ hookid }", replyHandler "del webhook:#{ hookid }"
|
|
|
|
###
|
|
Shuts down the db link.
|
|
|
|
@public shutDown()
|
|
###
|
|
exports.shutDown = () =>
|
|
@db?.quit()
|
|
# @db?.end()
|