Repository: ignite Updated Branches: refs/heads/master 737933e6d -> 1cfc9897f
IGNITE-8201 REST: Added AUTHENTICATE command. Fixed session tokens. Added new tests. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/1cfc9897 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/1cfc9897 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/1cfc9897 Branch: refs/heads/master Commit: 1cfc9897f41e8d8b48510029e7b650b2ed699c9a Parents: 737933e Author: Alexey Kuznetsov <akuznet...@apache.org> Authored: Tue Apr 17 11:46:45 2018 +0700 Committer: Alexey Kuznetsov <akuznet...@apache.org> Committed: Tue Apr 17 11:46:45 2018 +0700 ---------------------------------------------------------------------- .../client/suite/IgniteClientTestSuite.java | 6 +- .../JettyRestProcessorAbstractSelfTest.java | 60 ++++++++++----- ...ettyRestProcessorAuthenticationSelfTest.java | 45 ++--------- ...rocessorAuthenticationWithCredsSelfTest.java | 32 ++++++++ ...rocessorAuthenticationWithTokenSelfTest.java | 80 ++++++++++++++++++++ .../processors/rest/GridRestCommand.java | 3 + .../processors/rest/GridRestProcessor.java | 26 ++++--- .../auth/AuthenticationCommandHandler.java | 70 +++++++++++++++++ .../rest/handlers/auth/package-info.java | 22 ++++++ .../http/jetty/GridJettyRestHandler.java | 55 +++++++++++--- 10 files changed, 322 insertions(+), 77 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java b/modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java index 79fcf38..163f89a 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/client/suite/IgniteClientTestSuite.java @@ -49,7 +49,8 @@ import org.apache.ignite.internal.client.util.ClientByteUtilsTest; import org.apache.ignite.internal.client.util.ClientConsistentHashSelfTest; import org.apache.ignite.internal.client.util.ClientJavaHasherSelfTest; import org.apache.ignite.internal.processors.rest.ClientMemcachedProtocolSelfTest; -import org.apache.ignite.internal.processors.rest.JettyRestProcessorAuthenticationSelfTest; +import org.apache.ignite.internal.processors.rest.JettyRestProcessorAuthenticationWithCredsSelfTest; +import org.apache.ignite.internal.processors.rest.JettyRestProcessorAuthenticationWithTokenSelfTest; import org.apache.ignite.internal.processors.rest.JettyRestProcessorSignedSelfTest; import org.apache.ignite.internal.processors.rest.JettyRestProcessorUnsignedSelfTest; import org.apache.ignite.internal.processors.rest.RestBinaryProtocolSelfTest; @@ -87,7 +88,8 @@ public class IgniteClientTestSuite extends TestSuite { // Test jetty rest processor suite.addTestSuite(JettyRestProcessorSignedSelfTest.class); suite.addTestSuite(JettyRestProcessorUnsignedSelfTest.class); - suite.addTestSuite(JettyRestProcessorAuthenticationSelfTest.class); + suite.addTestSuite(JettyRestProcessorAuthenticationWithCredsSelfTest.class); + suite.addTestSuite(JettyRestProcessorAuthenticationWithTokenSelfTest.class); // Test TCP rest processor with original memcache client. suite.addTestSuite(ClientMemcachedProtocolSelfTest.class); http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java index 5dc44c4..e36447b 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAbstractSelfTest.java @@ -294,14 +294,19 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro /** * @param content Content to check. + * @return JSON node with actual response. */ - private JsonNode jsonCacheOperationResponse(String content, boolean bulk) throws IOException { + protected JsonNode assertResponseSucceeded(String content, boolean bulk) throws IOException { assertNotNull(content); assertFalse(content.isEmpty()); JsonNode node = JSON_MAPPER.readTree(content); - assertEquals(bulk, node.get("affinityNodeId").isNull()); + JsonNode affNode = node.get("affinityNodeId"); + + if (affNode != null) + assertEquals(bulk, affNode.isNull()); + assertEquals(STATUS_SUCCESS, node.get("successStatus").asInt()); assertTrue(node.get("error").isNull()); @@ -315,7 +320,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @param res Response. */ private void assertCacheOperation(String content, Object res) throws IOException { - JsonNode ret = jsonCacheOperationResponse(content, false); + JsonNode ret = assertResponseSucceeded(content, false); assertEquals(String.valueOf(res), ret.isObject() ? ret.toString() : ret.asText()); } @@ -325,7 +330,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @param res Response. */ private void assertCacheBulkOperation(String content, Object res) throws IOException { - JsonNode ret = jsonCacheOperationResponse(content, true); + JsonNode ret = assertResponseSucceeded(content, true); assertEquals(String.valueOf(res), ret.asText()); } @@ -334,7 +339,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @param content Content to check. */ private void assertCacheMetrics(String content) throws IOException { - JsonNode ret = jsonCacheOperationResponse(content, true); + JsonNode ret = assertResponseSucceeded(content, true); assertTrue(ret.isObject()); } @@ -349,7 +354,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro JsonNode node = JSON_MAPPER.readTree(content); - assertEquals(0, node.get("successStatus").asInt()); + assertEquals(STATUS_SUCCESS, node.get("successStatus").asInt()); assertTrue(node.get("error").isNull()); assertNotSame(securityEnabled(), node.get("sessionToken").isNull()); @@ -367,7 +372,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro JsonNode node = JSON_MAPPER.readTree(content); - assertEquals(0, node.get("successStatus").asInt()); + assertEquals(STATUS_SUCCESS, node.get("successStatus").asInt()); assertTrue(node.get("error").isNull()); assertFalse(node.get("response").isNull()); @@ -403,7 +408,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @throws IOException If failed. */ private void checkJson(String json, Person p) throws IOException { - JsonNode res = jsonCacheOperationResponse(json, false); + JsonNode res = assertResponseSucceeded(json, false); assertEquals(p.id.intValue(), res.get("id").asInt()); assertEquals(p.getOrganizationId().intValue(), res.get("orgId").asInt()); @@ -455,7 +460,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro info("Get command result: " + ret); - JsonNode res = jsonCacheOperationResponse(ret, false); + JsonNode res = assertResponseSucceeded(ret, false); assertEquals("Alex", res.get("NAME").asText()); assertEquals(300, res.get("SALARY").asInt()); @@ -476,7 +481,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro info("Get command result: " + ret); - JsonNode json = jsonCacheOperationResponse(ret, false); + JsonNode json = assertResponseSucceeded(ret, false); assertEquals(ref1.name, json.get("name").asText()); ref2.ref(ref1); @@ -552,7 +557,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro info("Get command result: " + ret); - JsonNode res = jsonCacheOperationResponse(ret, false); + JsonNode res = assertResponseSucceeded(ret, false); assertEquals(p.id, res.get("id").asInt()); assertEquals(p.name, res.get("name").asText()); @@ -637,7 +642,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro info("Get command result: " + ret); - JsonNode res = jsonCacheOperationResponse(ret, false); + JsonNode res = assertResponseSucceeded(ret, false); assertEquals(t.getKey(), res.get("key").asText()); assertEquals(t.getValue(), res.get("value").asText()); @@ -775,11 +780,11 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro info("Get all command result: " + ret); - JsonNode res = jsonCacheOperationResponse(ret, true); + JsonNode res = assertResponseSucceeded(ret, true); assertTrue(res.isObject()); - assertTrue(entries.equals(JSON_MAPPER.treeToValue(res, Map.class))); + assertEquals(entries, JSON_MAPPER.treeToValue(res, Map.class)); } /** @@ -973,10 +978,25 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro assertCacheOperation(ret, true); } + /** */ + private void failIgnite_5874() { + DataStorageConfiguration dsCfg = ignite(0).configuration().getDataStorageConfiguration(); + + if (dsCfg.getDefaultDataRegionConfiguration().isPersistenceEnabled()) + fail("IGNITE-5874"); + + for (DataRegionConfiguration dataRegCfg : dsCfg.getDataRegionConfigurations()) { + if (dataRegCfg.isPersistenceEnabled()) + fail("IGNITE-5874"); + } + } + /** * @throws Exception If failed. */ public void testPutWithExpiration() throws Exception { + failIgnite_5874(); + String ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_PUT, "key", "putKey", "val", "putVal", @@ -1013,6 +1033,8 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @throws Exception If failed. */ public void testAddWithExpiration() throws Exception { + failIgnite_5874(); + String ret = content(DEFAULT_CACHE_NAME, GridRestCommand.CACHE_ADD, "key", "addKey", "val", "addVal", @@ -1100,7 +1122,7 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro assertNull(jcache().localPeek("rmvKey2")); assertNull(jcache().localPeek("rmvKey3")); assertNull(jcache().localPeek("rmvKey4")); - assertTrue(jcache().localSize() == 0); + assertEquals(0, jcache().localSize()); assertCacheBulkOperation(ret, true); } @@ -1152,6 +1174,8 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro * @throws Exception If failed. */ public void testReplaceWithExpiration() throws Exception { + failIgnite_5874(); + jcache().put("replaceKey", "replaceVal"); assertEquals("replaceVal", jcache().get("replaceKey")); @@ -1353,20 +1377,20 @@ public abstract class JettyRestProcessorAbstractSelfTest extends AbstractRestPro assertNotNull(keyClasses); assertFalse(keyClasses.isNull()); - assertTrue(meta.keyClasses().equals(JSON_MAPPER.treeToValue(keyClasses, Map.class))); + assertEquals(meta.keyClasses(), JSON_MAPPER.treeToValue(keyClasses, Map.class)); JsonNode valClasses = item.get("valClasses"); assertNotNull(valClasses); assertFalse(valClasses.isNull()); - assertTrue(meta.valClasses().equals(JSON_MAPPER.treeToValue(valClasses, Map.class))); + assertEquals(meta.valClasses(), JSON_MAPPER.treeToValue(valClasses, Map.class)); JsonNode fields = item.get("fields"); assertNotNull(fields); assertFalse(fields.isNull()); - assertTrue(meta.fields().equals(JSON_MAPPER.treeToValue(fields, Map.class))); + assertEquals(meta.fields(), JSON_MAPPER.treeToValue(fields, Map.class)); JsonNode indexesByType = item.get("indexes"); http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationSelfTest.java index ca62091..27b8c03 100644 --- a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationSelfTest.java @@ -24,7 +24,6 @@ import org.apache.ignite.configuration.DataStorageConfiguration; import org.apache.ignite.configuration.IgniteConfiguration; import org.apache.ignite.internal.processors.authentication.IgniteAccessControlException; import org.apache.ignite.internal.processors.authentication.IgniteAuthenticationProcessor; -import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.testframework.GridTestUtils; @@ -33,18 +32,12 @@ import static org.apache.ignite.configuration.WALMode.NONE; /** * Test REST with enabled authentication. */ -public class JettyRestProcessorAuthenticationSelfTest extends JettyRestProcessorUnsignedSelfTest { +public abstract class JettyRestProcessorAuthenticationSelfTest extends JettyRestProcessorUnsignedSelfTest { /** */ - private static final String DFLT_LOGIN = "ignite"; + protected static final String DFLT_USER = "ignite"; /** */ - private static final String DFLT_PWD = "ignite"; - - /** */ - private String login = DFLT_LOGIN; - - /** */ - private String pwd = DFLT_PWD; + protected static final String DFLT_PWD = "ignite"; /** {@inheritDoc} */ @Override protected void beforeTestsStarted() throws Exception { @@ -54,11 +47,8 @@ public class JettyRestProcessorAuthenticationSelfTest extends JettyRestProcessor } /** {@inheritDoc} */ - @Override protected void beforeTest() throws Exception { - super.beforeTest(); - - login = DFLT_LOGIN; - pwd = DFLT_PWD; + @Override protected boolean securityEnabled() { + return true; } /** {@inheritDoc} */ @@ -97,32 +87,13 @@ public class JettyRestProcessorAuthenticationSelfTest extends JettyRestProcessor return cfg; } - /** {@inheritDoc} */ - @Override protected String restUrl() { - String url = super.restUrl(); - - if (!F.isEmpty(login)) { - url += "ignite.login=" + login; - - if (!F.isEmpty(pwd)) - url += "&ignite.password=" + pwd; - - url += '&'; - } - - return url; - } - /** * @throws Exception If failed. */ - public void testMissingCredentials() throws Exception { - login = null; - pwd = null; - - String ret = content(null, GridRestCommand.VERSION); + public void testAuthenticationCommand() throws Exception { + String ret = content(null, GridRestCommand.AUTHENTICATE); - assertResponseContainsError(ret, "The user name or password is incorrect"); + assertResponseSucceeded(ret, false); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithCredsSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithCredsSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithCredsSelfTest.java new file mode 100644 index 0000000..c75e8a9 --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithCredsSelfTest.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.rest; + +/** + * Test REST with enabled authentication and credentials in each request. + */ +public class JettyRestProcessorAuthenticationWithCredsSelfTest extends JettyRestProcessorAuthenticationSelfTest { + /** {@inheritDoc} */ + @Override protected String restUrl() { + String url = super.restUrl(); + + url += "ignite.login=" + DFLT_USER + "&ignite.password=" + DFLT_PWD + "&"; + + return url; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithTokenSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithTokenSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithTokenSelfTest.java new file mode 100644 index 0000000..5c046af --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/internal/processors/rest/JettyRestProcessorAuthenticationWithTokenSelfTest.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.rest; + +import org.apache.ignite.internal.util.typedef.F; + +/** + * Test REST with enabled authentication and token. + */ +public class JettyRestProcessorAuthenticationWithTokenSelfTest extends JettyRestProcessorAuthenticationSelfTest { + /** */ + private String tok = ""; + + /** {@inheritDoc} */ + @Override protected void beforeTest() throws Exception { + super.beforeTest(); + + // Authenticate and extract token. + if (F.isEmpty(tok)) { + String ret = content(null, GridRestCommand.AUTHENTICATE, + "user", DFLT_USER, + "password", DFLT_PWD); + + int p1 = ret.indexOf("sessionToken"); + int p2 = ret.indexOf('"', p1 + 16); + + tok = ret.substring(p1 + 15, p2); + } + } + + /** {@inheritDoc} */ + @Override protected String restUrl() { + String url = super.restUrl(); + + if (!F.isEmpty(tok)) + url += "sessionToken=" + tok + "&"; + + return url; + } + + /** + * @throws Exception If failed. + */ + public void testInvalidSessionToken() throws Exception { + tok = null; + + String ret = content(null, GridRestCommand.VERSION); + + assertResponseContainsError(ret, "Failed to handle request - session token not found or invalid"); + + tok = "InvalidToken"; + + ret = content(null, GridRestCommand.VERSION); + + assertResponseContainsError(ret, "Failed to handle request - session token not found or invalid"); + + tok = "26BE027D32CC42329DEC92D517B44E9E"; + + ret = content(null, GridRestCommand.VERSION); + + assertResponseContainsError(ret, "Failed to handle request - unknown session token (maybe expired session)"); + + tok = null; // Cleanup token for next tests. + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java index 0b9a662..265fe40 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestCommand.java @@ -169,6 +169,9 @@ public enum GridRestCommand { CLUSTER_CURRENT_STATE("currentstate"), /** */ + AUTHENTICATE("authenticate"), + + /** */ ADD_USER("adduser"), /** */ http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java index 4b8497e..da5e5c2 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/GridRestProcessor.java @@ -45,6 +45,7 @@ import org.apache.ignite.internal.processors.GridProcessorAdapter; import org.apache.ignite.internal.processors.authentication.AuthorizationContext; import org.apache.ignite.internal.processors.rest.client.message.GridClientTaskResultBean; import org.apache.ignite.internal.processors.rest.handlers.GridRestCommandHandler; +import org.apache.ignite.internal.processors.rest.handlers.auth.AuthenticationCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.cache.GridCacheCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.cluster.GridChangeStateCommandHandler; import org.apache.ignite.internal.processors.rest.handlers.datastructures.DataStructuresCommandHandler; @@ -81,6 +82,7 @@ import org.apache.ignite.plugin.security.SecurityPermission; import org.apache.ignite.thread.IgniteThread; import static org.apache.ignite.IgniteSystemProperties.IGNITE_REST_START_ON_CLIENT; +import static org.apache.ignite.internal.processors.rest.GridRestCommand.AUTHENTICATE; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_AUTH_FAILED; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_FAILED; import static org.apache.ignite.internal.processors.rest.GridRestResponse.STATUS_SECURITY_CHECK_FAILED; @@ -230,10 +232,11 @@ public class GridRestProcessor extends GridProcessorAdapter { try { ses = session(req); } + catch (IgniteAuthenticationException e) { + return new GridFinishedFuture<>(new GridRestResponse(STATUS_AUTH_FAILED, e.getMessage())); + } catch (IgniteCheckedException e) { - GridRestResponse res = new GridRestResponse(STATUS_FAILED, e.getMessage()); - - return new GridFinishedFuture<>(res); + return new GridFinishedFuture<>(new GridRestResponse(STATUS_FAILED, e.getMessage())); } assert ses != null; @@ -284,9 +287,9 @@ public class GridRestProcessor extends GridProcessorAdapter { throw new IgniteAuthenticationException("The user name or password is incorrect"); ses.authCtx = ctx.authentication().authenticate(login, pwd); - - req.authorizationContext(ses.authCtx); } + + req.authorizationContext(ses.authCtx); } catch (IgniteCheckedException e) { return new GridFinishedFuture<>(new GridRestResponse(STATUS_AUTH_FAILED, e.getMessage())); @@ -341,7 +344,7 @@ public class GridRestProcessor extends GridProcessorAdapter { assert res != null; - if (ctx.security().enabled() && !failed) + if ((authenticationEnabled || securityEnabled) && !failed) res.sessionTokenBytes(req.sessionToken()); interceptResponse(res, req); @@ -362,6 +365,10 @@ public class GridRestProcessor extends GridProcessorAdapter { while (true) { if (F.isEmpty(sesTok) && clientId == null) { + // TODO: In IGNITE 3.0 we should check credentials only for AUTHENTICATE command. + if (ctx.authentication().enabled() && req.command() != AUTHENTICATE && req.credentials() == null) + throw new IgniteAuthenticationException("Failed to handle request - session token not found or invalid"); + Session ses = Session.random(); UUID oldSesId = clientId2SesId.put(ses.clientId, ses.sesId); @@ -451,10 +458,7 @@ public class GridRestProcessor extends GridProcessorAdapter { try { sesExpTime = System.getProperty(IgniteSystemProperties.IGNITE_REST_SESSION_TIMEOUT); - if (sesExpTime != null) - sesExpTime0 = Long.valueOf(sesExpTime) * 1000; - else - sesExpTime0 = DEFAULT_SES_TIMEOUT; + sesExpTime0 = sesExpTime != null ? Long.valueOf(sesExpTime) * 1000 : DEFAULT_SES_TIMEOUT; } catch (NumberFormatException ignore) { U.warn(log, "Failed parsing IGNITE_REST_SESSION_TIMEOUT system variable [IGNITE_REST_SESSION_TIMEOUT=" @@ -504,6 +508,7 @@ public class GridRestProcessor extends GridProcessorAdapter { addHandler(new QueryCommandHandler(ctx)); addHandler(new GridLogCommandHandler(ctx)); addHandler(new GridChangeStateCommandHandler(ctx)); + addHandler(new AuthenticationCommandHandler(ctx)); addHandler(new UserActionCommandHandler(ctx)); // Start protocols. @@ -860,6 +865,7 @@ public class GridRestProcessor extends GridProcessorAdapter { case CLUSTER_CURRENT_STATE: case CLUSTER_ACTIVE: case CLUSTER_INACTIVE: + case AUTHENTICATE: case ADD_USER: case REMOVE_USER: case UPDATE_USER: http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/AuthenticationCommandHandler.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/AuthenticationCommandHandler.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/AuthenticationCommandHandler.java new file mode 100644 index 0000000..aa9bbbd --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/AuthenticationCommandHandler.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.rest.handlers.auth; + +import java.util.Collection; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.IgniteInternalFuture; +import org.apache.ignite.internal.processors.rest.GridRestCommand; +import org.apache.ignite.internal.processors.rest.GridRestResponse; +import org.apache.ignite.internal.processors.rest.handlers.GridRestCommandHandlerAdapter; +import org.apache.ignite.internal.processors.rest.request.GridRestRequest; +import org.apache.ignite.internal.util.future.GridFinishedFuture; +import org.apache.ignite.internal.util.typedef.internal.U; + +import static org.apache.ignite.internal.processors.rest.GridRestCommand.AUTHENTICATE; + +/** + * Authentication handler. + */ +public class AuthenticationCommandHandler extends GridRestCommandHandlerAdapter { + /** Commands. */ + private static final Collection<GridRestCommand> SUPPORTED_COMMANDS = U.sealList(AUTHENTICATE); + + /** + * @param ctx Context. + */ + public AuthenticationCommandHandler(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public Collection<GridRestCommand> supportedCommands() { + return SUPPORTED_COMMANDS; + } + + /** {@inheritDoc} */ + @Override public IgniteInternalFuture<GridRestResponse> handleAsync(GridRestRequest req) { + assert req != null; + + if (log.isDebugEnabled()) + log.debug("Handling topology REST request: " + req); + + try { + if (log.isDebugEnabled()) + log.debug("Handled topology REST request [req=" + req + ']'); + + return new GridFinishedFuture<>(new GridRestResponse(true)); + } + catch (Throwable e) { + log.error("Failed to handle REST request [req=" + req + ']', e); + + return new GridFinishedFuture<>(e); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/package-info.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/package-info.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/package-info.java new file mode 100644 index 0000000..1d58218 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/handlers/auth/package-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * <!-- Package description. --> + * REST authentication command. + */ +package org.apache.ignite.internal.processors.rest.handlers.auth; http://git-wip-us.apache.org/repos/asf/ignite/blob/1cfc9897/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java ---------------------------------------------------------------------- diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java index b3fbddd..99a8844 100644 --- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java +++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyRestHandler.java @@ -86,6 +86,12 @@ public class GridJettyRestHandler extends AbstractHandler { private static final String CHARSET = StandardCharsets.UTF_8.name(); /** */ + private static final String USER_PARAM = "user"; + + /** */ + private static final String PWD_PARAM = "password"; + + /** */ private static final String CACHE_NAME_PARAM = "cacheName"; /** */ @@ -100,10 +106,10 @@ public class GridJettyRestHandler extends AbstractHandler { /** */ private static final String WRITE_SYNCHRONIZATION_MODE_PARAM = "writeSynchronizationMode"; - /** */ + /**@deprecated Should be replaced with AUTHENTICATION + token in IGNITE 3.0 */ private static final String IGNITE_LOGIN = "ignite.login"; - /** */ + /**@deprecated Should be replaced with AUTHENTICATION + token in IGNITE 3.0 */ private static final String IGNITE_PASSWORD = "ignite.password"; /** */ @@ -731,6 +737,12 @@ public class GridJettyRestHandler extends AbstractHandler { break; } + case AUTHENTICATE: { + restReq = new GridRestRequest(); + + break; + } + case ADD_USER: case REMOVE_USER: case UPDATE_USER: { @@ -840,12 +852,9 @@ public class GridJettyRestHandler extends AbstractHandler { restReq.command(cmd); - if (params.containsKey(IGNITE_LOGIN) || params.containsKey(IGNITE_PASSWORD)) { - SecurityCredentials cred = new SecurityCredentials( - (String)params.get(IGNITE_LOGIN), (String)params.get(IGNITE_PASSWORD)); - - restReq.credentials(cred); - } + // TODO: In IGNITE 3.0 we should check credentials only for AUTHENTICATE command. + if (!credentials(params, IGNITE_LOGIN, IGNITE_PASSWORD, restReq)) + credentials(params, USER_PARAM, PWD_PARAM, restReq); String clientId = (String)params.get("clientId"); @@ -870,8 +879,13 @@ public class GridJettyRestHandler extends AbstractHandler { String sesTokStr = (String)params.get("sessionToken"); try { - if (sesTokStr != null) - restReq.sessionToken(U.hexString2ByteArray(sesTokStr)); + if (sesTokStr != null) { + // Token is a UUID encoded as 16 bytes as HEX. + byte[] bytes = U.hexString2ByteArray(sesTokStr); + + if (bytes.length == 16) + restReq.sessionToken(bytes); + } } catch (IllegalArgumentException ignored) { // Ignore invalid session token. @@ -881,6 +895,27 @@ public class GridJettyRestHandler extends AbstractHandler { } /** + * + * @param params Parameters. + * @param userParam Parameter name to take user name. + * @param pwdParam Parameter name to take password. + * @param restReq Request to add credentials if any. + * @return {@code true} If params contains credentials. + */ + private boolean credentials(Map<String, Object> params, String userParam, String pwdParam, GridRestRequest restReq) { + boolean hasCreds = params.containsKey(userParam) || params.containsKey(pwdParam); + + if (hasCreds) { + SecurityCredentials cred = new SecurityCredentials((String)params.get(userParam), + (String)params.get(pwdParam)); + + restReq.credentials(cred); + } + + return hasCreds; + } + + /** * Gets values referenced by sequential keys, e.g. {@code key1...keyN}. * * @param type Optional value type.