diff --git a/software/auth/cardAuth.coffee b/software/auth/cardAuth.coffee new file mode 100644 index 0000000..6738c98 --- /dev/null +++ b/software/auth/cardAuth.coffee @@ -0,0 +1,14 @@ +'use continuation' + +config = require "../config" +User = require "../models/user" + +module.exports = (fingerPrint, callback) -> + try + User.getByIdentity(fingerPrint, obtain(user)) + callback null, user.name + catch err + if err is 'no-such-user' + callback null, null + else + callback err \ No newline at end of file diff --git a/software/auth/cardAuth.js b/software/auth/cardAuth.js deleted file mode 100644 index e6e7e19..0000000 --- a/software/auth/cardAuth.js +++ /dev/null @@ -1,32 +0,0 @@ -var config = require("../config") -var request = require("request") - -module.exports = function(fingerPrint, callback){ - request.post({ - url: config.accounts9 + "/interface/getUserByIdentity", - json: { - interfaceSecret: config.interfaceSecret, - identity: fingerPrint - }, - strictSSL: true, - }, function(error, response, body){ - if(error){ - return callback(error); - } - if(body.error === 'no-such-user'){ - return callback(null, null); - }else if(body.error){ - return callback(body.error, null); - }else{ - if(body.user.groups.some(function(g){ - return config.acceptGroup.some(function(h){ - return h === g; - }); - })){ - return callback(null, body.user.name); - }else{ - return callback(null, null); - } - } - }); -} \ No newline at end of file diff --git a/software/card9.js b/software/card9.js index 453a471..c7917ae 100755 --- a/software/card9.js +++ b/software/card9.js @@ -1,4 +1,7 @@ -#!/usr/bin/env node +#!/bin/sh + +//; exec continuation $0 -e -c + var net = require('net'); var config = require('./config'); var logger = require('./logger'); @@ -7,6 +10,9 @@ var cardAuth = require('./auth/cardAuth'); var encoder = require('./utils/ProtocolBinarify'); var local = require('./utils/LocalServer'); + +var globalEvent = require('./globalEvent'); + var server = net.createServer(function(c) { //'connection' listener var client = c.remoteAddress + ':' + c.remotePort; logger({ @@ -71,6 +77,7 @@ reason: 'Client', data: p.data.param, }); + globalEvent.emit('cardScan', p.data.param); cardAuth(p.data.param, function(err, pass){ if(err){ logger({ diff --git a/software/card9ctl b/software/card9ctl deleted file mode 100755 index e057e33..0000000 --- a/software/card9ctl +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env node - -var defines = require('./utils/defines'); -var config = require('./config'); -var net = require('net'); - -function usage(){ - process.stderr.write( -'一个控制 Card9 的工具 \n\ -用法: card9ctl \n\ - \n\ - 可以是: \n\ - doorOpen 打开门禁 \n\ - alarmOff 消除警报音 \n\ -', 'utf-8'); -} - -var command = -1; -switch(process.argv[2]){ - case 'doorOpen': - command = defines.commands.doDoorOpen; - break; - case 'alarmOff': - command = defines.commands.doAlarmOff; - break; - default: - usage(); - process.stderr.once('drain', function(){ - process.exit(1); - }); - break; -} -if(command != -1){ - var client = net.createConnection({path: config.sockFile}, function(){ - client.write(new Buffer([command])); - client.once('drain', function(){ - client.end(); - console.log("成功发送命令。"); - }); - }).once('error', function(err){ - console.error("在通过“" + config.sockFile + "”与守护进程建立连接时,发生了错误:"); - console.error(err); - console.error("可能权限不足或守护进程未启动。"); - process.exit(1); - }); -} \ No newline at end of file diff --git a/software/card9ctl.js b/software/card9ctl.js new file mode 100755 index 0000000..3bac5b7 --- /dev/null +++ b/software/card9ctl.js @@ -0,0 +1,96 @@ +"use continuation" + + +var defines = require('./utils/defines'); +var config = require('./config'); +var net = require('net'); + +globalEvent = new (require('events').EventEmitter); + +function usage(){ + process.stderr.write( +'一个控制 Card9 的工具 \n\ +用法: card9ctl \n\ + \n\ + 可以是: \n\ + doorOpen 打开门禁 \n\ + alarmOff 消除警报音 \n\ + addCard 添加卡片\n\ +', 'utf-8'); +} + +var command = -1; +switch(process.argv[2]){ + case 'doorOpen': + command = defines.commands.doDoorOpen; + break; + case 'alarmOff': + command = defines.commands.doAlarmOff; + break; + case 'addCard': + if(process.argv[3]){ + command = -2; + break; + } + default: + usage(); + process.stderr.once('drain', function(){ + process.exit(1); + }); + break; +} + +var createSession = function(callback){ + var client = net.createConnection({path: config.sockFile}, callback).once('error', function(err){ + console.error("在通过“" + config.sockFile + "”与守护进程建立连接时,发生了错误:"); + console.error(err); + console.error("可能权限不足或守护进程未启动。"); + process.exit(1); + }); + return client; +} +if(command == -2){ + var username = process.argv[3]; + var lenBuffer = new Buffer(4); + var readLenth = 0; + var identity, identityLength; + var client = createSession(function(){ + try{ + var User = require('./models/user'); + var user; + User.getOrCreateByName(process.argv[3], obtain(user)); + console.log("等待刷卡..."); + globalEvent.on('card', cont(identity)); + user.addIdentity(identity, obtain()); + console.log("成功"); + process.exit(0); + }catch(err){ + console.error("错误:" + err); + process.exit(0); + } + }).on('data', function(chunk){ + for(var i=0; i < chunk.length; i++){ + if(readLenth < 4){ + lenBuffer[readLenth++] = chunk[i]; + if(readLenth == 4){ + identityLength = lenBuffer.readUInt32LE(0); + identity = new Buffer(identityLength); + } + }else{ + identity[ 3 - (--identityLength)] = chunk[i]; + if(identityLength == 0){ + globalEvent.emit('card', identity); + readLenth = 0; + } + } + } + }); +}else if(command != -1){ + var client = createSession(function(){ + client.write(new Buffer([command])); + client.once('drain', function(){ + client.end(); + console.log("成功发送命令。"); + }); + }); +} \ No newline at end of file diff --git a/software/config.example.js b/software/config.example.js index b580027..6af069f 100644 --- a/software/config.example.js +++ b/software/config.example.js @@ -3,7 +3,7 @@ bindPort : 57005, logFile : "./access.log", sockFile : "/tmp/card9.sock", - accounts9 : "https://accounts.net9.org", + dbAddr : "mongodb://localhost/card9", setUid : "", //should not be root; should have access to the source code interfaceSecret : "Example", acceptGroup: ["access-door"], diff --git a/software/globalEvent.coffee b/software/globalEvent.coffee new file mode 100644 index 0000000..d6509ab --- /dev/null +++ b/software/globalEvent.coffee @@ -0,0 +1 @@ +module.exports = new (require('events').EventEmitter) \ No newline at end of file diff --git a/software/models/user.coffee b/software/models/user.coffee new file mode 100644 index 0000000..fe5fc02 --- /dev/null +++ b/software/models/user.coffee @@ -0,0 +1,99 @@ +'use continuation' +mongoose = require("../utils/db") + +UserSchema = new mongoose.Schema( + name: + type: String + index: true + unique: true + identity: [ Buffer ] +) + +mongoose.model "User", UserSchema +module.exports = User = mongoose.model "User" + +UserSchema.pre 'save', (next) -> + @identity ?= [] + + next() + +User.checkName = (name, callback) -> + User.findOne name: name, (err, user) -> + return callback(err) if err + if user + callback "username-occupied" + else + callback null + +User.checkIdentity = (identity, callback) -> + User.getByIdentity identity, (err, user) -> + if err is 'no-such-user' + callback null + else if err + callback err + else + callback 'identity-exist' + +User.getByName = (name, callback) -> + User.findOne name: name, (err, user) -> + return callback(err) if err + if not user + callback 'no-such-user' + else + callback null, user + +User.getByIdentity = (identity, callback) -> + User.findOne identity: $elemMatch: $eq: identity, (err, user) -> + return callback(err) if err + if not user + callback 'no-such-user' + else + callback null, user + +# Create a new user +User.create = (user, callback) -> + try + # Normalize username and email address to lower case + user.name = user.name.toLowerCase() + usernameRegex = /^[a-z][a-z0-9_]{3,11}$/ + if not usernameRegex.exec(user.name) + return callback("invalid-username") + user = new User(user) + callback null, user + catch err + callback err + + +User::addIdentity = (identity, callback) -> + @identity ?= [] + self = this + try + User.checkIdentity(identity, obtain()) + self.identity.push identity + self.save callback + catch err + callback err + +User::removeIdentity = (identity, callback) -> + i = 0 + while i < @identity.length + if @identity[i].equals identity + @identity = @identity.slice(0, i).concat(@identity.slice(i + 1)) + @save callback + return + i++ + callback "no-such-identity" + +User.getOrCreateByName = (name, callback) -> + try + User.getByName(name, obtain(user)) + callback null, user + catch err + try + if err is 'no-such-user' + User.create({name: name}, obtain(user)) + callback null, user + else + throw err + catch err2 + callback err2 diff --git a/software/package.json b/software/package.json index fe5b821..4cb0737 100644 --- a/software/package.json +++ b/software/package.json @@ -3,9 +3,12 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "sudo node ./card9.js" + "start": "sudo ./card9.js" }, "dependencies": { - "request": "^2.53.0" + "mongoose": "^4.0.1", + "request": "^2.53.0", + "coffee-script": "", + "continuation": "~0.1.6", } } diff --git a/software/utils/LocalServer.js b/software/utils/LocalServer.js index 7048ca5..f116059 100644 --- a/software/utils/LocalServer.js +++ b/software/utils/LocalServer.js @@ -1,15 +1,33 @@ var net = require('net'); var defines = require('./defines'); var fs = require('fs'); +var globalEvent = require('../globalEvent'); var server = net.createServer(function(c){ c.on('data', function(chunk){ server.emit('command', chunk[0]); c.end(); }); + c.on('end', function(){ + globalEvent.removeListener('cardScan', listenToCardScan); + }); + c.on('error', function(){ + globalEvent.removeListener('cardScan', listenToCardScan); + }) + + var listenToCardScan = function(ident){ + var length = new Buffer(4); + length.writeUInt32LE(ident.length, 0); + c.write(length); + c.write(ident); + } + + globalEvent.on('cardScan', listenToCardScan); + }); server.create = function(sockFile, callback){ fs.unlink(sockFile, function(){ server.listen(sockFile, callback); }); }; + module.exports = server; \ No newline at end of file diff --git a/software/utils/db.js b/software/utils/db.js new file mode 100644 index 0000000..e5fa9da --- /dev/null +++ b/software/utils/db.js @@ -0,0 +1,8 @@ +var settings = require('../config'); + +module.exports = mongoose = require('mongoose'); +mongoose.connected = false; +mongoose.connection.on('open', function(){ + mongoose.connected = true; +}); +mongoose.connect(settings.dbAddr);