Permalink
Browse files

Added authentication and authorization to the site.

Login process is with a variation of the "no password" method described by Ben Brown here http://notes.xoxco.com/post/27999787765/is-it-time-for-password-less-login
  • Loading branch information...
1 parent 5710dde commit c3634f7eae525e32c163af86e74ca5106c6dc4e2 @hectorcorrea committed Oct 4, 2012
Showing with 291 additions and 27 deletions.
  1. +12 −9 app.coffee
  2. +90 −0 models/authModel.coffee
  3. +1 −1 package.json
  4. +85 −0 routes/authRoutes.coffee
  5. +47 −14 routes/blogRoutes.coffee
  6. +6 −3 routes/siteRoutes.coffee
  7. +25 −0 util/dateUtil.coffee
  8. +17 −0 views/login.ejs
  9. +8 −0 views/loginPost.ejs
View
@@ -8,7 +8,7 @@ http = require 'http'
siteRoutes = require './routes/siteRoutes'
blogRoutes = require './routes/blogRoutes'
logRoutes = require './routes/logRoutes'
-
+authRoutes = require './routes/authRoutes'
app = express()
@@ -53,7 +53,7 @@ app.configure 'production', ->
# Application settings
-app.set "isReadOnly", if app.settings.env is "production" then true else false
+# TODO: make showDrafs depending on user logged in
app.set "dataOptions", {
dataPath: __dirname + "/data"
createDataFileIfNotFound: false
@@ -65,14 +65,11 @@ app.set "dataOptions", {
app.get '/', siteRoutes.home
app.get '/about', siteRoutes.about
-if not app.settings.isReadOnly
- # Only enable edits when in development (local)
- # until I integrate an authentication process
- app.get '/blog/new', blogRoutes.editNew
- app.post '/blog/new', blogRoutes.saveNew
+app.get '/blog/new', blogRoutes.editNew
+app.post '/blog/new', blogRoutes.saveNew
- app.get '/blog/edit/:topicUrl', blogRoutes.edit
- app.post '/blog/save/:id', blogRoutes.save
+app.get '/blog/edit/:topicUrl', blogRoutes.edit
+app.post '/blog/save/:id', blogRoutes.save
app.get '/blog/list', blogRoutes.viewAll
@@ -86,6 +83,12 @@ app.get '/blog/:topicUrl', blogRoutes.viewOne
app.get '/logs/current', logRoutes.viewCurrent
app.get '/logs/:logDate', logRoutes.viewSpecific
app.get '/logs/', logRoutes.viewCurrent
+
+app.get '/login/:key', authRoutes.loginConfirm
+app.get '/login', authRoutes.loginGet
+app.post '/login', authRoutes.loginPost
+
+app.get '/logout', authRoutes.logout
app.get '*', siteRoutes.notFound
@@ -0,0 +1,90 @@
+fs = require 'fs'
+path = require 'path'
+dateUtil = require '../util/dateUtil'
+
+# TODO: Make this value configurable
+adminUserEmail = 'hector@hectorcorrea.com'
+
+_randomNumber = (max, min) ->
+ # http://stackoverflow.com/a/1527834/446681
+ r = Math.floor(Math.random() * (max - min + 1)) + min
+
+
+_randomUpperChar = ->
+ n = _randomNumber(65, 90); # A..Z
+ String.fromCharCode(n)
+
+
+_saveAuthData = (filePath, data) ->
+ text = JSON.stringify data, null, '\t'
+ fileName = path.join filePath, 'auth.json'
+ fs.writeFileSync fileName, text, 'utf8'
+ return
+
+
+getRandomKey = (length = 10) ->
+ key = ""
+ for i in [1..length]
+ key += _randomUpperChar()
+ key
+
+
+saveLoginKey = (filePath, key) ->
+ today = new Date()
+ expire = dateUtil.addHours today, 2
+ data = {
+ user: adminUserEmail
+ loginKey: key
+ loginKeyExpire: expire
+ }
+ _saveAuthData filePath, data
+
+
+saveAuthToken = (filePath, token) ->
+ data = {
+ user: adminUserEmail
+ authToken: token
+ }
+ _saveAuthData filePath, data
+
+
+clearAuthData = (filePath) ->
+ data = {
+ user: adminUserEmail
+ }
+ _saveAuthData filePath, data
+
+
+readAuthData = (filePath) ->
+ fileName = path.join filePath, 'auth.json'
+ if fs.existsSync(fileName)
+ text = fs.readFileSync fileName, 'utf8'
+ data = JSON.parse text
+ else
+ # TODO: initialize with admin user name.
+ # data = { user: adminUserEmail }
+ data = {}
+ return data
+
+
+isAuthenticated = (req, filePath) ->
+ authenticated = false
+ cookie = req.cookies.session
+ if cookie?.authToken?
+ console.log "cookie value found"
+ authData = readAuthData filePath
+ if authData.authToken?
+ console.log "data value found"
+ authenticated = true if authData.authToken is cookie.authToken
+ console.log "authenticated? #{authenticated}"
+ return authenticated
+
+
+module.exports = {
+ getRandomKey: getRandomKey
+ saveLoginKey: saveLoginKey
+ saveAuthToken: saveAuthToken
+ clearAuthData: clearAuthData
+ readAuthData: readAuthData
+ isAuthenticated: isAuthenticated
+}
View
@@ -1,6 +1,6 @@
{
"name": "simple-blog",
- "version": "0.0.1-10",
+ "version": "0.0.1-11",
"private": true,
"dependencies": {
"express": "= 3.0.0rc4",
@@ -0,0 +1,85 @@
+{Logger} = require '../util/logger'
+authModel = require '../models/authModel'
+
+_setAuthCookie = (res, authToken) ->
+ userInfo = {authToken: authToken}
+
+ oneHr = 60 * 60 # in seconds
+ oneDay = oneHr * 24
+ oneMonth = oneDay * 30
+ res.cookie 'session', userInfo, {maxAge: oneMonth}
+
+
+_clearAuthCookie = (res) ->
+ res.clearCookie 'session'
+
+
+loginGet = (req, res) ->
+ Logger.info 'authRoutes:loginGet'
+
+ dataPath = res.app.settings.dataOptions.dataPath
+ authData = authModel.readAuthData dataPath
+ if authData.user? is false
+ Logger.info "***************************************"
+ Logger.info "No user name has been configured in the"
+ Logger.info "auth.json data file. All login attempts"
+ Logger.info "will fail."
+ Logger.info "***************************************"
+ res.render 'login'
+
+
+loginPost = (req, res) ->
+ Logger.info 'authRoutes:loginPost'
+
+ email = req.body?.email ? ''
+ dataPath = res.app.settings.dataOptions.dataPath
+ authData = authModel.readAuthData dataPath
+
+ if email is authData.user
+ loginKey = authModel.getRandomKey()
+ #TODO: e-mail loginKey and remove from Logging!!!
+ Logger.info "#{loginKey}"
+ authModel.saveLoginKey dataPath, loginKey
+ Logger.info "e-mail with loginKey has been sent"
+ res.render 'loginPost'
+ else
+ Logger.warn "Invalid login attempt. E-mail received [#{email}]"
+ res.render 'login', {errorMsg: 'The e-mail indicated is not valid.'}
+
+
+loginConfirm = (req, res) ->
+ Logger.info 'authRoutes:loginConfirm'
+ dataPath = res.app.settings.dataOptions.dataPath
+ loginKey = req.params.key
+
+ if loginKey
+ authData = authModel.readAuthData dataPath
+ if authData.loginKey? and authData.loginKey is loginKey
+ authToken = authModel.getRandomKey()
+ _setAuthCookie res, authToken
+ authModel.saveAuthToken dataPath, authToken
+ Logger.info 'Woo-hoo! your are in.'
+ res.redirect '/'
+ else
+ Logger.warn "loginKey received [#{loginKey}] is not valid"
+ res.redirect '/'
+ else
+ Logger.warn 'No loginKey received'
+ res.redirect '/'
+
+
+logout = (req, res) ->
+ Logger.info 'authRoutes:logout'
+ dataPath = res.app.settings.dataOptions.dataPath
+ authModel.clearAuthData dataPath
+ _clearAuthCookie res
+ res.redirect '/'
+
+
+module.exports = {
+ loginGet: loginGet
+ loginPost: loginPost
+ loginConfirm: loginConfirm
+ logout: logout
+}
+
Oops, something went wrong.

0 comments on commit c3634f7

Please sign in to comment.