From a37709828332057cbfe7efa25fe5200b77f572dd Mon Sep 17 00:00:00 2001 From: Arno Uhlig Date: Fri, 23 Oct 2015 17:44:02 +0200 Subject: [PATCH] domain scoped authentication with tests --- .../api/identity/v3/KeystoneTests.java | 89 +++++- .../identity.v3/authv3_domainId_userId.json | 101 +++++++ .../identity.v3/authv3_domainId_userName.json | 46 +++ .../identity.v3/authv3_domainName_userId.json | 262 ++++++++++++++++++ .../identity.v3/authv3_unscoped.json | 17 ++ .../identity/domain/v3/AccessWrapper.java | 48 +++- .../identity/domain/v3/KeystoneGroup.java | 13 +- .../identity/domain/v3/KeystoneProject.java | 24 +- .../identity/domain/v3/KeystoneRole.java | 8 + .../identity/domain/v3/KeystoneService.java | 8 + .../identity/domain/v3/KeystoneUser.java | 33 ++- .../openstack/internal/OSAuthenticator.java | 65 ++--- 12 files changed, 658 insertions(+), 56 deletions(-) create mode 100644 core-test/src/main/resources/identity.v3/authv3_domainId_userId.json create mode 100644 core-test/src/main/resources/identity.v3/authv3_domainId_userName.json create mode 100644 core-test/src/main/resources/identity.v3/authv3_domainName_userId.json create mode 100644 core-test/src/main/resources/identity.v3/authv3_unscoped.json diff --git a/core-test/src/main/java/org/openstack4j/api/identity/v3/KeystoneTests.java b/core-test/src/main/java/org/openstack4j/api/identity/v3/KeystoneTests.java index d6c1efa56..b4941c548 100644 --- a/core-test/src/main/java/org/openstack4j/api/identity/v3/KeystoneTests.java +++ b/core-test/src/main/java/org/openstack4j/api/identity/v3/KeystoneTests.java @@ -19,6 +19,10 @@ public class KeystoneTests extends AbstractTest { private static final String JSON_AUTH_PROJECT = "/identity.v3/authv3_project.json"; + private static final String JSON_AUTH_DOMAINID_USERID = "/identity.v3/authv3_domainId_userId.json"; + private static final String JSON_AUTH_DOMAINID_USERNAME = "/identity.v3/authv3_domainId_userName.json"; + private static final String JSON_AUTH_DOMAINNAME_USERID = "/identity.v3/authv3_domainName_userId.json"; + private static final String JSON_AUTH_UNSCOPED = "/identity.v3/authv3_unscoped.json"; private static final String JSON_AUTH_TOKEN = "/identity.v3/authv3_token.json"; private static final ImmutableMap HEADER_AUTH_PROJECT_RESPONSE = ImmutableMap.of("X-Subject-Token", "763fd7e197ab4e00b2e6e0a8d22a8e87", "Content-Type", "application/json"); private static final ImmutableMap HEADER_AUTH_TOKEN_RESPONSE = ImmutableMap.of("X-Subject-Token", "3ecb5c2063904566be4b10406c0f7568", "Content-Type", "application/json"); @@ -27,6 +31,8 @@ public class KeystoneTests extends AbstractTest { private String userName = "admin"; private String userId = "aa9f25defa6d4cafb48466df83106065"; private String projectId = "123ac695d4db400a9001b91bb3b8aa46"; + private String domainId = "default"; + private String domainName = "Default"; private String projectDomainId = "default"; private String password = "test"; @@ -45,7 +51,6 @@ protected Service service() { * * @throws Exception */ - @Test(enabled = true) public void authenticate_userId_password_projectId_projectDomainId_Test() throws Exception { respondWith(JSON_AUTH_PROJECT); @@ -65,14 +70,13 @@ public void authenticate_userId_password_projectId_projectDomainId_Test() throws * * @throws Exception */ - @Test(enabled = true) public void authenticate_userName_password_projectId_projectDomainId_Test() throws Exception { respondWith(JSON_AUTH_PROJECT); associateClient(OSFactory.builderV3() .endpoint(authURL("/v3")) - .credentials(userName, password) + .credentials(userName, password, Identifier.byId(projectDomainId)) .scopeToProject(Identifier.byId(projectId), Identifier.byId(projectDomainId)) .authenticate()); @@ -80,12 +84,88 @@ public void authenticate_userName_password_projectId_projectDomainId_Test() thro assertEquals(osv3().getAccess().getUser().getId(), userId); } + /** + * authenticates with userId+password+domainId + * + * @throws Exception + */ + public void authenticate_userId_password_domainId_Test() throws Exception { + + respondWith(JSON_AUTH_DOMAINID_USERID); + + associateClient(OSFactory.builderV3() + .endpoint(authURL("/v3")) + .credentials(userId, password) + .scopeToDomain(Identifier.byId(domainId)) + .authenticate()); + + assertEquals(osv3().getToken().getVersion(), AuthVersion.V3); + assertEquals(osv3().getAccess().getUser().getId(), userId); + + } + + /** + * authenticates with userName+password+domainId + * + * @throws Exception + */ + public void authenticate_userName_password_domainId_Test() throws Exception { + + respondWith(JSON_AUTH_DOMAINID_USERNAME); + + associateClient(OSFactory.builderV3() + .endpoint(authURL("/v3")) + .credentials(userId, password, Identifier.byId(domainId)) + .scopeToDomain(Identifier.byId(domainId)) + .authenticate()); + + assertEquals(osv3().getToken().getVersion(), AuthVersion.V3); + assertEquals(osv3().getAccess().getUser().getId(), userId); + + } + + /** + * authenticates with userId+password+domainName + * + * @throws Exception + */ + public void authenticate_userId_password_domainName_Test() throws Exception { + + respondWith(JSON_AUTH_DOMAINNAME_USERID); + + associateClient(OSFactory.builderV3() + .endpoint(authURL("/v3")) + .credentials(userId, password) + .scopeToDomain(Identifier.byName(domainName)) + .authenticate()); + + assertEquals(osv3().getToken().getVersion(), AuthVersion.V3); + assertEquals(osv3().getAccess().getUser().getId(), userId); + + } + + /** + * try to authenticate unscoped. should return an UnsupportedOperationException saying it's not implemented yet. + * + * @throws Exception + */ + @Test(expectedExceptions={UnsupportedOperationException.class}) + public void authenticate_unscoped_exception_Test() throws Exception { + + respondWith(JSON_AUTH_UNSCOPED); + + OSFactory.builderV3() + .endpoint(authURL("/v3")) + .credentials(userId, password) + .authenticate(); + + } + /** * authenticates with a token+projectId+projectDomainId * * @throws Exception */ - @Test(enabled = true) public void auth_Token_Test() throws Exception { respondWithHeaderAndResource(HEADER_AUTH_PROJECT_RESPONSE, 201, JSON_AUTH_PROJECT); @@ -113,4 +193,5 @@ public void auth_Token_Test() throws Exception { assertEquals(osv3().getAccess().getUser().getId(), userId); } + } diff --git a/core-test/src/main/resources/identity.v3/authv3_domainId_userId.json b/core-test/src/main/resources/identity.v3/authv3_domainId_userId.json new file mode 100644 index 000000000..1b8656694 --- /dev/null +++ b/core-test/src/main/resources/identity.v3/authv3_domainId_userId.json @@ -0,0 +1,101 @@ +{ +"token": { + "domain": { + "id": "default", + "name": "Default" + }, + "methods": ["password"], + "roles": [{ + "id": "6ead57f8ae124996af8b0beb72ff1007", + "name": "admin" + }], + "expires_at": "2015-08-26T14:14:09.200971Z", + "catalog": [ + { + "endpoints":[{ + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "public", + "id": "6e82c8912d3f49a09df51035681d564c" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "admin", + "id": "7e44d321ae80457abc3728fa1e6feb32" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "internal", + "id": "c9a090a4597040849c03bc13588167f6" + }], + "type": "image", + "id": "0d56500210a24c38a3702b6825e24164", + "name": "glance" + }, + { + "endpoints": [], + "type": "volumev2", + "id": "2b92e79c45254516932c633229cd0e8b", + "name": "cinderv2" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "admin", + "id": "1ce26a6fffd0424bac135b9c68055b6e" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "public", + "id": "98db699b9ffa4dffb027d78163aad8cc" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "internal", + "id": "ece52860cf1e4eb6a8fed05c47a30147" + }], + "type": "ec2", + "id": "3364a7b95c664bf89a7a8db081576364", + "name": "ec2" + }, + { + "endpoints": + [], "type": "volume", "id": "511b94ce0482484ea09028091dd5e9a5", "name": "cinder"}, + {"endpoints": [], "type": "compute", "id": "5b7028751ed045d79467c7845ecb8c58", + "name": "nova"}, {"endpoints": [], "type": "computev21", "id": "97e665cbada043718180c5a6316df76a", + "name": "novav21"}, {"endpoints": [{"region_id": "RegionOne", "url": "http://devstack.openstack.stack:35357/v3", + "region": "RegionOne", "interface": "admin", "id": "185eda94de9340e58245062f75d7f80e"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "internal", "id": "9abd6797844d455f875af9537325cba4"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "public", "id": "d3b31f24e4ea40699f731e29e625c187"}], + "type": "identity", "id": "b577d8f7c7074d04a1165fcca638b600", "name": "keystone_v3x"}, + {"endpoints": [{"region_id": "europe", "url": "http://devstack.openstack.stack:35357/v3", + "region": "europe", "interface": "admin", "id": "32bb2c6aea944ea6b4956eb24142d2e2"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "public", "id": "480ea71dc8cf4c959df1c6304be87056"}, + {"region_id": "europe", "url": "http://devstack.openstack.stack:5000/v3", "region": + "europe", "interface": "public", "id": "600638643d22494fad4f30e3b22ae124"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "internal", "id": "8a254651925e4a3e9505c863a00c017e"}, + {"region_id": "europe", "url": "http://devstack.openstack.stack:5000/v3", "region": + "europe", "interface": "internal", "id": "b93da6aaba654d8cb451ff8378d7d2a5"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:35357/v3", "region": + "RegionOne", "interface": "admin", "id": "d5f8e0da0f3345529a5fb324d735d4a3"}], + "type": "identity_v3", "id": "cd9002bbadfe495d81b5ee4c50768009", "name": "keystone_v3"}], + "extras": {}, "user": {"domain": {"id": "default", "name": "Default"}, "id": + "aa9f25defa6d4cafb48466df83106065", "name": "admin"}, "audit_ids": ["gdkPX0P8RfChxa18M4Y2Tg"], + "issued_at": "2015-08-26T13:14:09.201023Z"} +} \ No newline at end of file diff --git a/core-test/src/main/resources/identity.v3/authv3_domainId_userName.json b/core-test/src/main/resources/identity.v3/authv3_domainId_userName.json new file mode 100644 index 000000000..77e4e98ea --- /dev/null +++ b/core-test/src/main/resources/identity.v3/authv3_domainId_userName.json @@ -0,0 +1,46 @@ +{ +"token": {"domain": {"id": "default", "name": "Default"}, "methods": + ["password"], "roles": [{"id": "6ead57f8ae124996af8b0beb72ff1007", "name": + "admin"}], "expires_at": "2015-08-26T14:14:09.335998Z", "catalog": [{"endpoints": + [{"region_id": "RegionOne", "url": "http://devstack.openstack.stack:9292", "region": + "RegionOne", "interface": "public", "id": "6e82c8912d3f49a09df51035681d564c"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:9292", "region": + "RegionOne", "interface": "admin", "id": "7e44d321ae80457abc3728fa1e6feb32"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:9292", "region": + "RegionOne", "interface": "internal", "id": "c9a090a4597040849c03bc13588167f6"}], + "type": "image", "id": "0d56500210a24c38a3702b6825e24164", "name": "glance"}, + {"endpoints": [], "type": "volumev2", "id": "2b92e79c45254516932c633229cd0e8b", + "name": "cinderv2"}, {"endpoints": [{"region_id": "RegionOne", "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", "interface": "admin", "id": "1ce26a6fffd0424bac135b9c68055b6e"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:8773/", "region": + "RegionOne", "interface": "public", "id": "98db699b9ffa4dffb027d78163aad8cc"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:8773/", "region": + "RegionOne", "interface": "internal", "id": "ece52860cf1e4eb6a8fed05c47a30147"}], + "type": "ec2", "id": "3364a7b95c664bf89a7a8db081576364", "name": "ec2"}, {"endpoints": + [], "type": "volume", "id": "511b94ce0482484ea09028091dd5e9a5", "name": "cinder"}, + {"endpoints": [], "type": "compute", "id": "5b7028751ed045d79467c7845ecb8c58", + "name": "nova"}, {"endpoints": [], "type": "computev21", "id": "97e665cbada043718180c5a6316df76a", + "name": "novav21"}, {"endpoints": [{"region_id": "RegionOne", "url": "http://devstack.openstack.stack:35357/v3", + "region": "RegionOne", "interface": "admin", "id": "185eda94de9340e58245062f75d7f80e"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "internal", "id": "9abd6797844d455f875af9537325cba4"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "public", "id": "d3b31f24e4ea40699f731e29e625c187"}], + "type": "identity", "id": "b577d8f7c7074d04a1165fcca638b600", "name": "keystone_v3x"}, + {"endpoints": [{"region_id": "europe", "url": "http://devstack.openstack.stack:35357/v3", + "region": "europe", "interface": "admin", "id": "32bb2c6aea944ea6b4956eb24142d2e2"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "public", "id": "480ea71dc8cf4c959df1c6304be87056"}, + {"region_id": "europe", "url": "http://devstack.openstack.stack:5000/v3", "region": + "europe", "interface": "public", "id": "600638643d22494fad4f30e3b22ae124"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:5000/v3", "region": + "RegionOne", "interface": "internal", "id": "8a254651925e4a3e9505c863a00c017e"}, + {"region_id": "europe", "url": "http://devstack.openstack.stack:5000/v3", "region": + "europe", "interface": "internal", "id": "b93da6aaba654d8cb451ff8378d7d2a5"}, + {"region_id": "RegionOne", "url": "http://devstack.openstack.stack:35357/v3", "region": + "RegionOne", "interface": "admin", "id": "d5f8e0da0f3345529a5fb324d735d4a3"}], + "type": "identity_v3", "id": "cd9002bbadfe495d81b5ee4c50768009", "name": "keystone_v3"}], + "extras": {}, "user": {"domain": {"id": "default", "name": "Default"}, "id": + "aa9f25defa6d4cafb48466df83106065", "name": "admin"}, "audit_ids": ["m5vsr2xgRuKNQezeN78VAw"], + "issued_at": "2015-08-26T13:14:09.336033Z"} +} \ No newline at end of file diff --git a/core-test/src/main/resources/identity.v3/authv3_domainName_userId.json b/core-test/src/main/resources/identity.v3/authv3_domainName_userId.json new file mode 100644 index 000000000..ae562a588 --- /dev/null +++ b/core-test/src/main/resources/identity.v3/authv3_domainName_userId.json @@ -0,0 +1,262 @@ +{ + "token": { + "domain": { + "id": "default", + "name": "Default" + }, + "methods": ["password"], + "roles": [{ + "id": "6ead57f8ae124996af8b0beb72ff1007", + "name": "admin" + }], + "expires_at": "2015-08-26T14:14:09.416956Z", + "catalog": [{ + "endpoints": [ + { + "region_id": "RegionOne", + "url":"http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "public", + "id": "6e82c8912d3f49a09df51035681d564c" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "admin", + "id": "7e44d321ae80457abc3728fa1e6feb32" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:9292", + "region": "RegionOne", + "interface": "internal", + "id": "c9a090a4597040849c03bc13588167f6" + }], + "type": "image", + "id": "0d56500210a24c38a3702b6825e24164", + "name": "glance" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "internal", + "id": "261aaf6239bb49a4a1cfa87c19859138" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "admin", + "id": "437d282e0bb94622aaacc4d194c069a9" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "public", + "id": "5e78bf7bae7c4ff5b9720b2c2e4da743" + }], + "type": "volumev2", + "id": "2b92e79c45254516932c633229cd0e8b", + "name": "cinderv2" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "admin", + "id": "1ce26a6fffd0424bac135b9c68055b6e" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "public", + "id": "98db699b9ffa4dffb027d78163aad8cc" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8773/", + "region": "RegionOne", + "interface": "internal", + "id": "ece52860cf1e4eb6a8fed05c47a30147" + }], + "type": "ec2", + "id": "3364a7b95c664bf89a7a8db081576364", + "name": "ec2" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "admin", + "id": "4442fbd064844a7bbe6a792507d4b8e3" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "internal", + "id": "4b4178fd2e3d4f329600cc4ceaaa7e3a" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8776/v1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "public", + "id": "90977723dba04ea9a2a184c99565ccff" + }], + "type": "volume", + "id": "511b94ce0482484ea09028091dd5e9a5", + "name": "cinder" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "internal", + "id": "81c51855280345e9a6c322ca986d4e4b" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "admin", + "id": "a0310a37cf6144a6a967cbae9a7959ba" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "public", + "id": "f6d38c03b9c04a9e924aaa288ce014b8" + }], + "type": "compute", + "id": "5b7028751ed045d79467c7845ecb8c58", + "name": "nova" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2.1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "internal", + "id": "2f17e155b0aa47838394e6c4f6fe30e0" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2.1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "public", + "id": "9d2555fd27dd44e5acfb5e56127d974b" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:8774/v2.1/123ac695d4db400a9001b91bb3b8aa46", + "region": "RegionOne", + "interface": "admin", + "id": "e8bdd9403fbb4efa8d77bfd4f6a5e34a" + }], + "type": "computev21", + "id": "97e665cbada043718180c5a6316df76a", + "name": "novav21" + }, + { + "endpoints": [ + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:35357/v3", + "region": "RegionOne", + "interface": "admin", + "id": "185eda94de9340e58245062f75d7f80e" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "RegionOne", + "interface": "internal", + "id": "9abd6797844d455f875af9537325cba4" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "RegionOne", + "interface": "public", + "id": "d3b31f24e4ea40699f731e29e625c187" + }], + "type": "identity", + "id": "b577d8f7c7074d04a1165fcca638b600", + "name": "keystone_v3x" + }, + { + "endpoints": [ + { + "region_id": "europe", + "url": "http://devstack.openstack.stack:35357/v3", + "region": "europe", + "interface": "admin", + "id": "32bb2c6aea944ea6b4956eb24142d2e2" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "RegionOne", + "interface": "public", + "id": "480ea71dc8cf4c959df1c6304be87056" + }, + { + "region_id": "europe", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "europe", + "interface": "public", + "id": "600638643d22494fad4f30e3b22ae124" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "RegionOne", + "interface": "internal", + "id": "8a254651925e4a3e9505c863a00c017e" + }, + { + "region_id": "europe", + "url": "http://devstack.openstack.stack:5000/v3", + "region": "europe", + "interface": "internal", + "id": "b93da6aaba654d8cb451ff8378d7d2a5" + }, + { + "region_id": "RegionOne", + "url": "http://devstack.openstack.stack:35357/v3", + "region": "RegionOne", + "interface": "admin", + "id": "d5f8e0da0f3345529a5fb324d735d4a3" + }], + "type": "identity_v3", + "id": "cd9002bbadfe495d81b5ee4c50768009", + "name": "keystone_v3" + }], + "extras": {}, + "user": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "aa9f25defa6d4cafb48466df83106065", + "name": "admin" + }, + "audit_ids": ["jcxRTXu9T5e9giMO57cE3A","FJwqoPgBQf-W3e9jqC3dcA"], + "issued_at": "2015-08-26T13:14:09.542711Z" + } +} \ No newline at end of file diff --git a/core-test/src/main/resources/identity.v3/authv3_unscoped.json b/core-test/src/main/resources/identity.v3/authv3_unscoped.json new file mode 100644 index 000000000..2ea5be45e --- /dev/null +++ b/core-test/src/main/resources/identity.v3/authv3_unscoped.json @@ -0,0 +1,17 @@ +{ + "token": { + "methods": ["password"], + "expires_at": "2015-08-26T14:14:10.395363Z", + "extras": {}, + "user": { + "domain": { + "id": "default", + "name": "Default" + }, + "id": "aa9f25defa6d4cafb48466df83106065", + "name": "admin" + }, + "audit_ids": ["jMpsNK77TA6hwF6bbBHQYA"], + "issued_at": "2015-08-26T13:14:10.395414Z" + } +} \ No newline at end of file diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/AccessWrapper.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/AccessWrapper.java index ca6534b66..4cca5daf5 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/AccessWrapper.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/AccessWrapper.java @@ -14,6 +14,7 @@ import org.openstack4j.model.identity.Tenant; import org.openstack4j.model.identity.TokenV2; import org.openstack4j.model.identity.v3.Catalog; +import org.openstack4j.model.identity.v3.Domain; import org.openstack4j.model.identity.v3.Project; import org.openstack4j.model.identity.v3.User; import org.openstack4j.openstack.identity.domain.KeystoneTenant; @@ -25,7 +26,7 @@ /** * This class wraps V3 objects and satisfies the original V2 API calls to bridge * the differences between V2 and V3 - * + * * @author Jeremy Unruh */ public class AccessWrapper implements Access { @@ -63,17 +64,40 @@ public static AccessWrapper wrap(KeystoneToken token) { */ @Override public org.openstack4j.model.identity.Token getToken() { - Project project = KeystoneProject.builder() - .id(token.getProject().getId()) - .name(token.getProject().getName()) - .build(); - Tenant tenant = KeystoneTenant.builder() - .id(project.getId()) - .name(project.getName()) - .description(project.getDescription()) - .enabled(project.isEnabled()) - .build(); - return new V2Token(id, token.getExpires(), token.getVersion(), tenant); + /*This is only a temporary solution to get authentication with project and domain scope working for now. + * Proper implementation coming soon. + */ + if(token.getProject() != null) { + Project project = KeystoneProject.builder() + .id(token.getProject().getId()) + .name(token.getProject().getName()) + .build(); + Tenant tenant = KeystoneTenant.builder() + .id(project.getId()) + .name(project.getName()) + .description(project.getDescription()) + .enabled(project.isEnabled()) + .build(); + + return new V2Token(id, token.getExpires(), token.getVersion(), tenant); + } + else if(token.getDomain() != null) { + Domain domain = KeystoneDomain.builder() + .id(token.getDomain().getId()) + .name(token.getDomain().getName()) + .build(); + Tenant tenant = KeystoneTenant.builder() + .id(domain.getId()) + .name(domain.getName()) + .description(domain.getDescription()) + .enabled(domain.isEnabled()) + .build(); + + return new V2Token(id, token.getExpires(), token.getVersion(), tenant); + } + else { + throw new UnsupportedOperationException("Unscoped authentication not yet supported"); + } } /** diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneGroup.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneGroup.java index 55abca9ca..0f73fbd96 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneGroup.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneGroup.java @@ -7,14 +7,18 @@ import org.openstack4j.model.identity.v3.Group; import org.openstack4j.openstack.common.ListResult; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; import com.google.common.base.Objects; /** * group model class for identity.v3 - * + * * @see API reference */ +@JsonRootName("group") +@JsonIgnoreProperties(ignoreUnknown = true) public class KeystoneGroup implements Group { private static final long serialVersionUID = 1L; @@ -39,6 +43,7 @@ public GroupBuilder toBuilder() { /** * @return the id of the group */ + @Override public String getId() { return id; } @@ -46,6 +51,7 @@ public String getId() { /** * @return the name of the group */ + @Override public String getName() { return name; } @@ -53,6 +59,7 @@ public String getName() { /** * @return the description of the group */ + @Override public String getDescription() { return description; } @@ -60,6 +67,7 @@ public String getDescription() { /** * @return the domainId of the group */ + @Override public String getDomainId() { return domainId; } @@ -67,6 +75,7 @@ public String getDomainId() { /** * @return the links of the group */ + @Override public Map getLinks() { return links; } @@ -74,6 +83,7 @@ public Map getLinks() { /** * {@inheritDoc} */ + @Override public String toString() { return Objects.toStringHelper(this).omitNullValues() .add("id", id) @@ -188,6 +198,7 @@ public static class Groups extends ListResult { @JsonProperty("groups") protected List list; + @Override public List value() { return list; } diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneProject.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneProject.java index 173047c32..5eea02e2d 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneProject.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneProject.java @@ -15,7 +15,7 @@ /** * Project model class for identity.v3 - * + * * @see API reference */ @JsonRootName("project") @@ -29,6 +29,7 @@ public class KeystoneProject implements Project { private String name; @JsonProperty private KeystoneDomain domain; + @JsonProperty("domain_id") private String domainId; private String description; private Map links; @@ -52,6 +53,7 @@ public ProjectBuilder toBuilder() { /** * @return the id of the project */ + @Override public String getId() { return id; } @@ -59,6 +61,7 @@ public String getId() { /** * @return the domain the project belongs to */ + @Override public Domain getDomain() { return domain; } @@ -66,6 +69,7 @@ public Domain getDomain() { /** * @return the domain id of the project */ + @Override public String getDomainId() { if (domainId == null && domain != null && domain.getId() != null) domainId = domain.getId(); @@ -75,6 +79,7 @@ public String getDomainId() { /** * @return the description of the project */ + @Override public String getDescription() { return description; } @@ -82,6 +87,7 @@ public String getDescription() { /** * @return the name of the project */ + @Override public String getName() { return name; } @@ -97,6 +103,7 @@ public Map getLinks() { /** * @return the parentId of the project */ + @Override public String getParentId() { return parentId; } @@ -104,6 +111,7 @@ public String getParentId() { /** * @return the subtree of the project */ + @Override public String getSubtree() { return subtree; } @@ -111,6 +119,7 @@ public String getSubtree() { /** * @return the parents of the project */ + @Override public String getParents() { return parents; } @@ -125,7 +134,7 @@ public boolean isEnabled() { /** * set project enabled - * + * * @param enabled * the new enabled status */ @@ -136,6 +145,7 @@ public void setEnabled(Boolean enabled) { /** * {@inheritDoc} */ + @Override public String toString() { return Objects.toStringHelper(this).omitNullValues() .add("id", id) @@ -193,6 +203,7 @@ public static class ProjectConcreteBuilder implements ProjectBuilder { /** * @see KeystoneProject#getId() */ + @Override public ProjectBuilder id(String id) { model.id = id; return this; @@ -201,6 +212,7 @@ public ProjectBuilder id(String id) { /** * @see KeystoneProject#getDomainId() */ + @Override public ProjectBuilder domain(Domain domain) { if (domain != null && domain.getId() != null) model.domainId = domain.getId(); @@ -210,6 +222,7 @@ public ProjectBuilder domain(Domain domain) { /** * @see KeystoneProject#getDescription() */ + @Override public ProjectBuilder description(String description) { model.description = description; return this; @@ -218,6 +231,7 @@ public ProjectBuilder description(String description) { /** * @see KeystoneProject#getName() */ + @Override public ProjectBuilder name(String name) { model.name = name; return this; @@ -226,6 +240,7 @@ public ProjectBuilder name(String name) { /** * @see KeystoneProject#getLinks() */ + @Override public ProjectBuilder links(Map links) { model.links = links; return this; @@ -234,6 +249,7 @@ public ProjectBuilder links(Map links) { /** * @see KeystoneProject#getParentId() */ + @Override public ProjectBuilder parentId(String parentId) { model.parentId = parentId; return this; @@ -242,6 +258,7 @@ public ProjectBuilder parentId(String parentId) { /** * @see KeystoneProject#getSubtree() */ + @Override public ProjectBuilder subtree(String subtree) { model.subtree = subtree; return this; @@ -250,6 +267,7 @@ public ProjectBuilder subtree(String subtree) { /** * @see KeystoneProject#getParents() */ + @Override public ProjectBuilder parents(String parents) { model.parents = parents; return this; @@ -258,6 +276,7 @@ public ProjectBuilder parents(String parents) { /** * @see KeystoneProject#isEnabled() */ + @Override public ProjectBuilder enabled(boolean enabled) { model.enabled = enabled; return this; @@ -295,6 +314,7 @@ public static class Projects extends ListResult { @JsonProperty("projects") protected List list; + @Override public List value() { return list; } diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneRole.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneRole.java index 965aa3d1e..7d7fba873 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneRole.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneRole.java @@ -7,12 +7,16 @@ import org.openstack4j.model.identity.v3.Role; import org.openstack4j.openstack.common.ListResult; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; import com.google.common.base.Objects; /** * v3 role implementation */ +@JsonRootName("role") +@JsonIgnoreProperties(ignoreUnknown = true) public class KeystoneRole implements Role { private static final long serialVersionUID = 1L; @@ -34,6 +38,7 @@ public static RoleBuilder builder() { /** * {@inheritDoc} */ + @Override public String getId() { return id; } @@ -41,6 +46,7 @@ public String getId() { /** * {@inheritDoc} */ + @Override public String getName() { return name; } @@ -48,6 +54,7 @@ public String getName() { /** * {@inheritDoc} */ + @Override public Map getLinks() { return links; } @@ -124,6 +131,7 @@ public static class Roles extends ListResult { @JsonProperty("roles") protected List list; + @Override public List value() { return list; } diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneService.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneService.java index 4ce888c8c..c1b0bb866 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneService.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneService.java @@ -10,12 +10,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; import com.google.common.base.Objects; /** * V3 OpenStack service * */ +@JsonRootName("service") @JsonIgnoreProperties(ignoreUnknown = true) public class KeystoneService implements Service { @@ -44,6 +46,7 @@ public static ServiceBuilder builder() { /** * {@inheritDoc} */ + @Override public String getId() { return id; } @@ -51,6 +54,7 @@ public String getId() { /** * {@inheritDoc} */ + @Override public String getDescription() { return description; } @@ -58,6 +62,7 @@ public String getDescription() { /** * {@inheritDoc} */ + @Override public String getName() { return name; } @@ -65,6 +70,7 @@ public String getName() { /** * {@inheritDoc} */ + @Override public String getType() { return type; } @@ -72,6 +78,7 @@ public String getType() { /** * {@inheritDoc} */ + @Override public List getEndpoints() { return endpoints; } @@ -79,6 +86,7 @@ public List getEndpoints() { /** * {@inheritDoc} */ + @Override public Map getLinks() { return links; } diff --git a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneUser.java b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneUser.java index 77181b201..0db869272 100644 --- a/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneUser.java +++ b/core/src/main/java/org/openstack4j/openstack/identity/domain/v3/KeystoneUser.java @@ -15,10 +15,8 @@ /** * User model class for identity.v3 - * - * @see API - * reference + * + * @see API reference */ @JsonRootName("user") @JsonIgnoreProperties(ignoreUnknown = true) @@ -31,10 +29,12 @@ public class KeystoneUser implements User { private String name; @JsonProperty private KeystoneDomain domain; + @JsonProperty("domain_id") private String domainId; private String email; private String password; private String description; + @JsonProperty("default_project_id") private String defaultProjectId; private Map links; private Boolean enabled = true; @@ -54,6 +54,7 @@ public UserBuilder toBuilder() { /** * @return the id of the user */ + @Override public String getId() { return id; } @@ -61,6 +62,7 @@ public String getId() { /** * @return the of the user */ + @Override public String getName() { return name; } @@ -68,6 +70,7 @@ public String getName() { /** * @return the email of the user */ + @Override public String getEmail() { return email; } @@ -75,6 +78,7 @@ public String getEmail() { /** * @return the password of the user */ + @Override public String getPassword() { return password; } @@ -82,6 +86,7 @@ public String getPassword() { /** * @return the description of the user */ + @Override public String getDescription() { return description; } @@ -89,6 +94,7 @@ public String getDescription() { /** * @return the domainId of the user */ + @Override public String getDomainId() { return domainId; } @@ -96,6 +102,7 @@ public String getDomainId() { /** * @return the domain of the user */ + @Override public Domain getDomain() { return domain; } @@ -103,6 +110,7 @@ public Domain getDomain() { /** * @return the defaultProjectId of the user */ + @Override public String getDefaultProjectId() { return defaultProjectId; } @@ -110,6 +118,7 @@ public String getDefaultProjectId() { /** * @return the links of the user */ + @Override public Map getLinks() { return links; } @@ -117,13 +126,14 @@ public Map getLinks() { /** * @return the enabled of the user */ + @Override public boolean isEnabled() { return enabled != null && enabled; } /** * set user enabled - * + * * @param enabled * the new enabled status */ @@ -131,6 +141,7 @@ public void setEnabled(Boolean enabled) { this.enabled = enabled; } + @Override public String toString() { return Objects.toStringHelper(this).omitNullValues() .add("name", name) @@ -151,6 +162,7 @@ public static class Users extends ListResult { @JsonProperty("users") private List list; + @Override public List value() { return list; } @@ -171,6 +183,7 @@ public static class UserConcreteBuilder implements UserBuilder { /** * @see KeystoneUser#getId() */ + @Override public UserBuilder id(String id) { model.id = id; return this; @@ -179,6 +192,7 @@ public UserBuilder id(String id) { /** * @return the KeystoneUser model */ + @Override public User build() { return model; } @@ -196,6 +210,7 @@ public UserBuilder from(User in) { /** * @see KeystoneUser#getName() */ + @Override public UserBuilder name(String name) { model.name = name; return this; @@ -204,6 +219,7 @@ public UserBuilder name(String name) { /** * @see KeystoneUser#getDefaultProjectId() */ + @Override public UserBuilder defaultProjectId(String defaultProjectId) { model.defaultProjectId = defaultProjectId; return this; @@ -212,6 +228,7 @@ public UserBuilder defaultProjectId(String defaultProjectId) { /** * @see KeystoneUser#getDomainId() */ + @Override public UserBuilder domainId(String domainId) { model.domainId = domainId; return this; @@ -220,6 +237,7 @@ public UserBuilder domainId(String domainId) { /** * @see KeystoneUser#getDomain() */ + @Override public UserBuilder domain(Domain domain) { // model.domain = domain; if (domain != null && domain.getId() != null) @@ -230,6 +248,7 @@ public UserBuilder domain(Domain domain) { /** * @see KeystoneUser#getEmail() */ + @Override public UserBuilder email(String email) { model.email = email; return this; @@ -238,6 +257,7 @@ public UserBuilder email(String email) { /** * @see KeystoneUser#getPassword() */ + @Override public UserBuilder password(String password) { model.password = password; return this; @@ -246,6 +266,7 @@ public UserBuilder password(String password) { /** * @see KeystoneUser#getLinks() */ + @Override public UserBuilder links(Map links) { model.links = links; return this; @@ -254,6 +275,7 @@ public UserBuilder links(Map links) { /** * @see KeystoneUser#isEnabled() */ + @Override public UserBuilder enabled(boolean enabled) { model.enabled = enabled; return this; @@ -262,6 +284,7 @@ public UserBuilder enabled(boolean enabled) { /** * @see KeystoneUser#getDescription() */ + @Override public UserBuilder description(String description) { model.description = description; return this; diff --git a/core/src/main/java/org/openstack4j/openstack/internal/OSAuthenticator.java b/core/src/main/java/org/openstack4j/openstack/internal/OSAuthenticator.java index d6606707f..059fcf6bf 100644 --- a/core/src/main/java/org/openstack4j/openstack/internal/OSAuthenticator.java +++ b/core/src/main/java/org/openstack4j/openstack/internal/OSAuthenticator.java @@ -28,7 +28,7 @@ /** * Responsible for authenticating and re-authenticating sessions for V2 and V3 of the Identity API - * + * * @author Jeremy Unruh */ public class OSAuthenticator { @@ -37,7 +37,7 @@ public class OSAuthenticator { private static final Logger LOG = LoggerFactory.getLogger(OSAuthenticator.class); /** * Invokes authentication to obtain a valid Token for either V2 and V3 - * + * * @param auth the authentication credentials * @param endpoint the identity endpoint * @param perspective the network facing perspective @@ -64,7 +64,7 @@ public static OSClient invoke(TokenAuth auth, String endpoint, Facing perspectiv SessionInfo info = new SessionInfo(endpoint, perspective, false, provider); return authenticateV2(auth, info, config); } - + /** * Invokes V3 authentication via an existing token * @param auth the token authentication @@ -77,17 +77,17 @@ public static OSClient invoke(KeystoneAuth auth, String endpoint, Facing perspec SessionInfo info = new SessionInfo(endpoint, perspective, false, provider); return authenticateV3(auth, info, config); } - - + + /** * Re-authenticates/renews the token for the current Session */ public static void reAuthenticate() { - + LOG.debug("Re-Authenticating session due to expired Token or invalid response"); - + OSClientSession session = OSClientSession.getCurrent(); - + switch (session.getAccess().getVersion()) { case V3: KeystoneToken token = session.getAccess().unwrap(); @@ -103,7 +103,7 @@ public static void reAuthenticate() { break; } } - + private static OSClient authenticateV2(org.openstack4j.openstack.identity.domain.Auth auth, SessionInfo info, Config config) { HttpRequest request = HttpRequest.builder(KeystoneAccess.class) .header(ClientConstants.HEADER_OS4J_AUTH, TOKEN_INDICATOR) @@ -113,7 +113,7 @@ private static OSClient authenticateV2(org.openstack4j.openstack.identity.domain .config(config) .entity(auth) .build(); - + HttpResponse response = HttpExecutor.create().execute(request); if (response.getStatus() >= 400) { try { @@ -123,9 +123,9 @@ private static OSClient authenticateV2(org.openstack4j.openstack.identity.domain HttpEntityHandler.closeQuietly(response); } } - + KeystoneAccess access = response.getEntity(KeystoneAccess.class); - + if (auth.getType() == Type.CREDENTIALS) { access = access.applyContext(info.endpoint, (Credentials) auth); } @@ -135,10 +135,10 @@ else if (auth.getType() == Type.RAX_APIKEY) { else { access = access.applyContext(info.endpoint, (TokenAuth) auth); } - + if (!info.reLinkToExistingSession) return OSClientSession.createSession(access, info.perspective, info.provider, config); - + OSClientSession current = OSClientSession.getCurrent(); current.access = access; return current; @@ -152,9 +152,9 @@ private static OSClient authenticateV3(KeystoneAuth auth, SessionInfo info, Conf .config(config) .entity(auth) .build(); - + HttpResponse response = HttpExecutor.create().execute(request); - + if (response.getStatus() >= 400) { try { @@ -164,46 +164,47 @@ private static OSClient authenticateV3(KeystoneAuth auth, SessionInfo info, Conf HttpEntityHandler.closeQuietly(response); } } - - //TODO: proper impl. without accesswrapper KeystoneToken token = response.getEntity(KeystoneToken.class); AccessWrapper accesswr = AccessWrapper.wrap(token); accesswr.setId(response.header(ClientConstants.HEADER_X_SUBJECT_TOKEN)); - + if (auth.getType() == Type.CREDENTIALS) { token = token.applyContext(info.endpoint, new org.openstack4j.openstack.identity.domain.v3.Credentials(auth.getUsername(), auth.getPassword())); } -// TODO: support for rackspace apikey extension -// else if (t_auth.getType() == Type.RAX_APIKEY) { -// token = token.applyContext(endpoint, new org.openstack4j.openstack.identity.domain.v3.Credentials(auth.getUsername(), auth.getPassword())); -// } else { - //TODO v3 TokenAuth or via warpper - token = token.applyContext( info.endpoint, new TokenAuth(accesswr.getToken().getId(), auth.getScope().getProject().getName(), auth.getScope().getProject().getId())); + if( token.getProject() != null ) { + token = token.applyContext( info.endpoint, new TokenAuth(accesswr.getToken().getId(), auth.getScope().getProject().getName(), auth.getScope().getProject().getId())); + } + else if( token.getDomain() != null) { + token = token.applyContext( info.endpoint, new TokenAuth(accesswr.getToken().getId(), auth.getScope().getDomain().getName(), auth.getScope().getDomain().getId())); + } + else { + throw new UnsupportedOperationException("Unscoped authentication not yet supported"); + } } - + if (!info.reLinkToExistingSession) return OSClientSession.createSession(accesswr, info.perspective, info.provider, config); - + OSClientSession current = OSClientSession.getCurrent(); current.access = accesswr; return current; - + } - + private static class SessionInfo { String endpoint; Facing perspective; boolean reLinkToExistingSession; CloudProvider provider; - + SessionInfo(String endpoint, Facing perspective, boolean reLinkToExistingSession, CloudProvider provider) { this.endpoint = endpoint; this.perspective = perspective; this.reLinkToExistingSession = reLinkToExistingSession; this.provider = provider; } - + } - + } \ No newline at end of file