diff --git a/lib/authService/authenticate.js b/lib/authService/authenticate.js index a87abb5b..58e39d37 100644 --- a/lib/authService/authenticate.js +++ b/lib/authService/authenticate.js @@ -33,8 +33,38 @@ function verifyAndDecodeToken(token) { }); } -class Authenticate { +function getRealm(token) { + // issuer has to contain realm id, e.g.: http:///auth/realms/iff + const parts = token.iss.split("/"); + return parts[parts.length - 1]; +} + +function validate(token, username) { + let type = token.type; + let did = token.device_id; + if (!type || !did) { + return false; + } + if (type !== "device" || did !== username) { + return false; + } + return true; +} + +function legacyValidate(token, username) { + let accounts = token.accounts; + let type = token.type; + let did = token.sub; + if (!accounts || !type || !did) { + return false; + } + if (accounts.length !== 1 || accounts[0].role !== "device" || type !== "device" || did !== username) { + return false; + } + return true; +} +class Authenticate { constructor(config, logger){ this.config = config; this.logger = logger; @@ -47,7 +77,6 @@ class Authenticate { } // expects "username" and "password" as url-query-parameters async authenticate(req, res) { - this.logger.debug("Auth request " + JSON.stringify(req.query)); var username = req.query.username; var token = req.query.password; @@ -63,21 +92,29 @@ class Authenticate { res.sendStatus(400); return; } - // check whether accounts contains only one element and role is device - var accounts = decoded_token.accounts; - var did = decoded_token.sub; - if (accounts === undefined || did === undefined || accounts.length !== 1 || accounts[0].role !== "device" || did !== username) { + if (!validate(decoded_token, username) && !legacyValidate(decoded_token, username)) { res.sendStatus(400); return; } - var accountId = accounts[0].id; - //put account/device into the list of accepted topics - var key = accountId + "/" + did; - await this.cache.setValue(key, "acl", true); - - // For SparkplugB put account/gateway(node) into the list of accepted topics to authenticate Node/gateway messages - if (decoded_token.gateway !== undefined ||decoded_token.gateway === null) { - var gatewayKey=accountId + "/"+ decoded_token.gateway; + // check whether accounts contains only one element and role is device + var accounts = decoded_token.accounts; + var did = decoded_token.device_id ? decoded_token.device_id : decoded_token.sub; + var accountId = accounts ? accounts[0].id : null; + let realm = getRealm(decoded_token); + // put realm/device into the list of accepted topics + await this.cache.setValue(realm + "/" + did, "acl", true); + // put account/device into the list of accepted topics (legacy) + if (accountId) { + var key = accountId + "/" + did; + await this.cache.setValue(key, "acl", true); + } + // For SparkplugB put (legacy) account/gateway(node) and realm/gateway(node) into the list of accepted topics to authenticate Node/gateway messages + if (decoded_token.gateway !== undefined || decoded_token.gateway === null) { + if (accountId) { + var legacyGatewayKey = accountId + "/" + decoded_token.gateway; + await this.cache.setValue(legacyGatewayKey, "acl", true); + } + let gatewayKey = realm + "/" + decoded_token.gateway; await this.cache.setValue(gatewayKey, "acl", true); } res.sendStatus(200); diff --git a/test/unit/lib_authServiceTest.js b/test/unit/lib_authServiceTest.js index f18d309b..7aaec548 100644 --- a/test/unit/lib_authServiceTest.js +++ b/test/unit/lib_authServiceTest.js @@ -120,6 +120,8 @@ describe(fileToTest, function(){ it('Authentication shall successfully validate a token', function(done){ var decodedToken = { sub: "deviceId", + iss: "http://keycloak-url/auth/realms/realmId", + type: "device", accounts: [{ role: "device", id: "accountId" @@ -149,7 +151,7 @@ describe(fileToTest, function(){ }; var cache = { setValue: function(key, type, value) { - assert.equal(key, "accountId/deviceId", "Wrong cache value received."); + assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received."); assert.equal(type, "acl", "Wrong cache value received."); assert.equal(value, true, "Wrong cache value received."); } @@ -174,7 +176,9 @@ describe(fileToTest, function(){ it('Authentication shall successfully validate a token with gatewayid', function(done){ var decodedToken = { sub: "deviceId", + iss: "http://keycloak-url/auth/realms/realmId", gateway: "gatewayId", + type: "device", accounts: [{ role: "device", id: "accountId" @@ -204,7 +208,8 @@ describe(fileToTest, function(){ }; var cache = { setValue: function(key, type, value) { - assert.oneOf(key,["accountId/deviceId" , "accountId/gatewayId"], "Wrong cache value received."); + console.log(key, type, value); + assert.oneOf(key,["accountId/deviceId", "realmId/deviceId", "accountId/gatewayId", "realmId/gatewayId"], "Wrong cache value received."); assert.equal(type, "acl", "Wrong cache value received."); assert.equal(value, true, "Wrong cache value received."); return; @@ -230,6 +235,8 @@ describe(fileToTest, function(){ it('Authentication shall detect wrong deviceId in username', function(done){ var decodedToken = { sub: "deviceId", + iss: "http://keycloak-url/auth/realms/realmId", + type: "device", accounts: [{ role: "device", id: "accountId" @@ -260,7 +267,7 @@ describe(fileToTest, function(){ }; var cache = { setValue: function(key, type, value) { - assert.equal(key, "accountId/deviceId", "Wrong cache value received."); + assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received."); assert.equal(type, "acl", "Wrong cache value received."); assert.equal(value, true, "Wrong cache value received."); } @@ -284,6 +291,8 @@ describe(fileToTest, function(){ it('Authentication shall detect wrong role in token', function(done){ var decodedToken = { sub: "deviceId", + iss: "http://keycloak-url/auth/realms/realmId", + type: "device", accounts: [{ role: "wrontRole", id: "accountId" @@ -314,7 +323,7 @@ describe(fileToTest, function(){ }; var cache = { setValue: function(key, type, value) { - assert.equal(key, "accountId/deviceId", "Wrong cache value received."); + assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received."); assert.equal(type, "acl", "Wrong cache value received."); assert.equal(value, true, "Wrong cache value received."); } @@ -338,6 +347,8 @@ describe(fileToTest, function(){ it('Authentication shall detect wrong account array', function(done){ var decodedToken = { sub: "deviceId", + iss: "http://keycloak-url/auth/realms/realmId", + type: "device", accounts: [{ role: "device", id: "accountId" @@ -371,7 +382,7 @@ describe(fileToTest, function(){ }; var cache = { setValue: function(key, type, value) { - assert.equal(key, "accountId/deviceId", "Wrong cache value received."); + assert.oneOf(key,["accountId/deviceId", "realmId/deviceId"], "Wrong cache value received."); assert.equal(type, "acl", "Wrong cache value received."); assert.equal(value, true, "Wrong cache value received."); }