From 269f57dec12b4cf740c5cd4626bc4a817fe3b13e Mon Sep 17 00:00:00 2001 From: Zaid Abdulla Date: Mon, 20 Aug 2018 01:22:45 -0700 Subject: [PATCH] Convert auth.coffee to JS --- .gitignore | 1 - lib/auth.coffee | 92 ----------------------------------------- lib/auth.js | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 93 deletions(-) delete mode 100644 lib/auth.coffee create mode 100644 lib/auth.js diff --git a/.gitignore b/.gitignore index 9f346bb..6a5c7ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *~ lib/*.map lib/api-functions.js -lib/auth.js lib/cwmp.js lib/db.js lib/nbi.js diff --git a/lib/auth.coffee b/lib/auth.coffee deleted file mode 100644 index 4a4639f..0000000 --- a/lib/auth.coffee +++ /dev/null @@ -1,92 +0,0 @@ -### -# Copyright 2013-2017 Zaid Abdulla -# -# This file is part of GenieACS. -# -# GenieACS is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# GenieACS is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with GenieACS. If not, see . -### - -crypto = require 'crypto' - - -exports.parseAuthHeader = (authHeader) -> - authHeader = authHeader.trim() - method = authHeader.split(' ', 1)[0] - res = {method: method} - parts = authHeader.slice(method.length + 1).split(',') - - while (part = parts.shift())? - name = part.split('=', 1)[0] - throw new Error('Unable to parse auth header') if name.length == part.length - value = part.slice(name.length + 1) - if not /^\s*"/.test(value) - value = value.trim() - else - while not /[^\\]"\s*$/.test(value) - p = parts.shift() - throw new Error('Unable to parse auth header') if not p? - value += ',' + p - - try - value = JSON.parse(value) - catch - throw new Error('Unable to parse auth header') - - res[name.trim()] = value - - return res - - -exports.basic = (username, password) -> - "Basic #{new Buffer("#{username}:#{password}").toString('base64')}" - - -exports.digest = (username, password, uri, httpMethod, body, authHeader) -> - cnonce = '0a4f113b' - nc = '00000001' - - if authHeader.qop? - if authHeader.qop.indexOf(',') != -1 - qop = 'auth' # either auth or auth-int. prefer auth - else - qop = authHeader.qop - - ha1 = crypto.createHash('md5') - ha1.update(username).update(':').update(authHeader.realm).update(':').update(password) - # TODO support "MD5-sess" algorithm directive - ha1 = ha1.digest('hex') - - ha2 = crypto.createHash('md5') - ha2.update(httpMethod).update(':').update(uri) - if qop == 'auth-int' - ha2.update(':').update(body) - ha2 = ha2.digest('hex') - - response = crypto.createHash('md5') - response.update(ha1).update(':').update(authHeader.nonce) - - if qop? - response.update(':').update(nc).update(':').update(cnonce).update(':').update(qop) - response.update(':').update(ha2) - response = response.digest('hex') - - authString = "Digest username=\"#{username}\"" - authString += ",realm=\"#{authHeader.realm}\"" - authString += ",nonce=\"#{authHeader.nonce}\"" - authString += ",uri=\"#{uri}\"" - authString += ",algorithm=#{authHeader.algorithm}" if authHeader.algorithm? - authString += ",qop=#{qop},nc=#{nc},cnonce=\"#{cnonce}\"" if qop? - authString += ",response=\"#{response}\"" - authString += ",opaque=\"#{authHeader.opaque}\"" if authHeader.opaque? - return authString diff --git a/lib/auth.js b/lib/auth.js new file mode 100644 index 0000000..c390269 --- /dev/null +++ b/lib/auth.js @@ -0,0 +1,107 @@ +/** + * Copyright 2013-2018 Zaid Abdulla + * + * This file is part of GenieACS. + * + * GenieACS is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * GenieACS is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with GenieACS. If not, see . + */ +"use strict"; + +const crypto = require("crypto"); + +function parseAuthHeader(authHeader) { + authHeader = authHeader.trim(); + const method = authHeader.split(" ", 1)[0]; + const res = {method: method}; + const parts = authHeader.slice(method.length + 1).split(","); + + let part; + while ((part = parts.shift()) != null) { + const name = part.split("=", 1)[0]; + if (name.length === part.length) + throw new Error("Unable to parse auth header"); + + let value = part.slice(name.length + 1); + if (!/^\s*"/.test(value)) { + value = value.trim(); + } else { + while (!/[^\\]"\s*$/.test(value)) { + const p = parts.shift(); + if (p == null) + throw new Error("Unable to parse auth header"); + value += "," + p; + } + + try { + value = JSON.parse(value); + } catch (error) { + throw new Error("Unable to parse auth header"); + } + } + res[name.trim()] = value; + } + return res; +} + +function basic(username, password) { + return "Basic " + (new Buffer(`${username}:${password}`).toString("base64")); +} + +function digest(username, password, uri, httpMethod, body, authHeader) { + const cnonce = "0a4f113b"; + const nc = "00000001"; + let qop; + + if (authHeader.qop) { + if (authHeader.qop.indexOf(",") !== -1) + qop = "auth"; // Either auth or auth-int, prefer auth + else + qop = authHeader.qop; + } + + let ha1 = crypto.createHash("md5"); + ha1.update(username).update(":").update(authHeader.realm).update(":").update(password); + // TODO support "MD5-sess" algorithm directive + ha1 = ha1.digest("hex"); + + let ha2 = crypto.createHash("md5"); + ha2.update(httpMethod).update(":").update(uri); + if (qop === "auth-int") + ha2.update(":").update(body); + ha2 = ha2.digest("hex"); + + let hash = crypto.createHash("md5"); + hash.update(ha1).update(":").update(authHeader.nonce); + if (qop) + hash.update(":").update(nc).update(":").update(cnonce).update(":").update(qop); + hash.update(":").update(ha2); + + let authString = `Digest username="${username}"`; + authString += `,realm="${authHeader.realm}"`; + authString += `,nonce="${authHeader.nonce}"`; + authString += `,uri="${url}"`; + if (authHeader.algorithm) + authString += `,algorithm=${authHeader.algorithm}`; + if (qop) + authString += `,qop=${qop},nc=${nc},cnonce="${cnonce}"`; + authString += `,response="${hash.digest("hex")}"`; + if (authHeader.opaque) + authString += `,opaque="${authHeader.opaque}"`; + + return authString; +} + +exports.parseAuthHeader = parseAuthHeader; +exports.basic = basic; +exports.digest = digest;