diff --git a/modules/clients/src/test/java/org/apache/ignite/internal/client/integration/ClientTcpAuthTest.java b/modules/clients/src/test/java/org/apache/ignite/internal/client/integration/ClientTcpAuthTest.java new file mode 100644 index 0000000000000..f24cd1bf21cf7 --- /dev/null +++ b/modules/clients/src/test/java/org/apache/ignite/internal/client/integration/ClientTcpAuthTest.java @@ -0,0 +1,104 @@ +/* + * 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.client.integration; + +import org.apache.ignite.internal.client.GridClient; +import org.apache.ignite.internal.client.GridClientCompute; +import org.apache.ignite.internal.client.GridClientConfiguration; +import org.apache.ignite.internal.client.GridClientData; +import org.apache.ignite.internal.client.GridClientException; +import org.apache.ignite.internal.client.GridClientNode; +import org.apache.ignite.internal.client.GridClientPredicate; +import org.apache.ignite.internal.client.GridClientProtocol; +import org.apache.ignite.internal.client.ssl.GridSslContextFactory; +import org.apache.ignite.plugin.security.SecurityCredentials; +import org.apache.ignite.plugin.security.SecurityCredentialsBasicProvider; +import org.apache.ignite.spi.discovery.tcp.TestAuthPluginProvider; +import org.apache.ignite.spi.discovery.tcp.TestAuthProcessor; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Collections; + +/** + * Tests the Authorization in client-server communication. + */ +public class ClientTcpAuthTest extends ClientAbstractSelfTest { + + @Override + protected GridClientConfiguration clientConfiguration() throws GridClientException { + GridClientConfiguration cliCfg = super.clientConfiguration(); + cliCfg.setSecurityCredentialsProvider( + new SecurityCredentialsBasicProvider( + new SecurityCredentials("user", "password"))); + return cliCfg; + } + + @Test + public void testAuthorization() throws Exception{ + GridClient client = client(); + + GridClientData data = client.data("cache"); + data.put("key", "val"); + assertEquals("val", data.get("key")); + + GridClientCompute compute = client.compute().projection(new GridClientPredicate() { + @Override public boolean apply(GridClientNode e) { + return true; + } + }); + + Integer result = compute.execute(getTaskName(), Collections.singletonList("taskArg")); + assertNotNull(result); + assertEquals(7, result.intValue()); + } + + /** {@inheritDoc} */ + @Override protected GridClientProtocol protocol() { + return GridClientProtocol.TCP; + } + + /** {@inheritDoc} */ + @Override protected String serverAddress() { + return HOST + ":" + BINARY_PORT; + } + + /** {@inheritDoc} */ + @Override protected boolean useSsl() { + return false; + } + + /** {@inheritDoc} */ + @Override protected GridSslContextFactory sslContextFactory() { + return null; + } + + @BeforeClass + public static void enableAuth(){ + TestAuthPluginProvider.enabled = true; + TestAuthProcessor.enabled = true; + } + + @AfterClass + public static void disableAuth(){ + TestAuthPluginProvider.enabled = false; + TestAuthProcessor.enabled = false; + } + +} \ No newline at end of file diff --git a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java index 8a9b0f1850963..1f1a82d91db1f 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/client/impl/connection/GridClientNioTcpConnection.java @@ -469,7 +469,19 @@ else if (now - lastPingSndTime > pingInterval && lastPingRcvTime != Long.MAX_VAL assert old == null; - GridNioFuture sndFut = ses.send(msg); + GridNioFuture sndFut; + if(sesTok == null && credentials() != null) { + fut.retryState(TcpClientFuture.STATE_AUTH_RETRY); + + GridClientAuthenticationRequest req = buildAuthRequest(); + + req.requestId(reqId); + + sndFut = ses.send(req); + } + else { + sndFut = ses.send(msg); + } lastMsgSndTime = System.currentTimeMillis(); diff --git a/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider b/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider index 5805dfd95409a..b23f2ce4b7066 100644 --- a/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider +++ b/modules/core/src/test/java/META-INF/services/org.apache.ignite.plugin.PluginProvider @@ -1,4 +1,5 @@ org.apache.ignite.spi.discovery.tcp.TestReconnectPluginProvider +org.apache.ignite.spi.discovery.tcp.TestAuthPluginProvider org.apache.ignite.internal.processors.cache.persistence.standbycluster.IgniteStandByClusterTest$StanByClusterTestProvider org.apache.ignite.internal.processors.cache.persistence.wal.memtracker.PageMemoryTrackerPluginProvider org.apache.ignite.internal.processors.configuration.distributed.TestDistibutedConfigurationPlugin diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthPluginProvider.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthPluginProvider.java new file mode 100644 index 0000000000000..cefb3e89166ca --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthPluginProvider.java @@ -0,0 +1,119 @@ +/* + * 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.spi.discovery.tcp; + +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.IgniteKernal; +import org.apache.ignite.internal.processors.security.GridSecurityProcessor; +import org.apache.ignite.plugin.CachePluginContext; +import org.apache.ignite.plugin.CachePluginProvider; +import org.apache.ignite.plugin.ExtensionRegistry; +import org.apache.ignite.plugin.IgnitePlugin; +import org.apache.ignite.plugin.PluginContext; +import org.apache.ignite.plugin.PluginProvider; +import org.apache.ignite.plugin.PluginValidationException; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; +import java.util.UUID; + +/** + * Creates TestAuthProcessor. + */ +public class TestAuthPluginProvider implements PluginProvider { + /** */ + private GridKernalContext igniteCtx; + + /** */ + public static volatile boolean enabled; + + /** {@inheritDoc} */ + @Override public String name() { + return "TestAuthPlugin"; + } + + /** {@inheritDoc} */ + @Override public String version() { + return "1.0"; + } + + /** {@inheritDoc} */ + @Override public String copyright() { + return ""; + } + + /** {@inheritDoc} */ + @Override public void initExtensions(PluginContext ctx, ExtensionRegistry registry) { + igniteCtx = ((IgniteKernal)ctx.grid()).context(); + } + + /** {@inheritDoc} */ + @Override public void start(PluginContext ctx) throws IgniteCheckedException { + // No-op + } + + /** {@inheritDoc} */ + @Override public void stop(boolean cancel) throws IgniteCheckedException { + // No-op + } + + /** {@inheritDoc} */ + @Override public void onIgniteStart() throws IgniteCheckedException { + // No-op + } + + /** {@inheritDoc} */ + @Override public void onIgniteStop(boolean cancel) { + // No-op + } + + /** {@inheritDoc} */ + @Nullable @Override public Serializable provideDiscoveryData(UUID nodeId) { + return null; + } + + /** {@inheritDoc} */ + @Override public void receiveDiscoveryData(UUID nodeId, Serializable data) { + // No-op + } + + /** {@inheritDoc} */ + @Override public void validateNewNode(ClusterNode node) throws PluginValidationException { + // No-op + } + + /** {@inheritDoc} */ + @Nullable @Override public Object createComponent(PluginContext ctx, Class cls) { + if (enabled && GridSecurityProcessor.class.equals(cls)) + return new TestAuthProcessor(igniteCtx); + + return null; + } + + /** {@inheritDoc} */ + @Override public IgnitePlugin plugin() { + return new IgnitePlugin() {}; + } + + /** {@inheritDoc} */ + @Override public CachePluginProvider createCacheProvider(CachePluginContext ctx) { + return null; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthProcessor.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthProcessor.java new file mode 100644 index 0000000000000..f020396c7c258 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TestAuthProcessor.java @@ -0,0 +1,272 @@ +/* + * 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.spi.discovery.tcp; + +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.GridKernalContext; +import org.apache.ignite.internal.processors.GridProcessorAdapter; +import org.apache.ignite.internal.processors.security.GridSecurityProcessor; +import org.apache.ignite.internal.processors.security.SecurityContext; +import org.apache.ignite.plugin.security.AuthenticationContext; +import org.apache.ignite.plugin.security.SecurityCredentials; +import org.apache.ignite.plugin.security.SecurityException; +import org.apache.ignite.plugin.security.SecurityPermission; +import org.apache.ignite.plugin.security.SecurityPermissionSet; +import org.apache.ignite.plugin.security.SecuritySubject; +import org.apache.ignite.plugin.security.SecuritySubjectType; +import org.jetbrains.annotations.Nullable; + +import java.io.Serializable; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; + +/** + * Updates node attributes on disconnect. + */ +public class TestAuthProcessor extends GridProcessorAdapter implements GridSecurityProcessor { + /** Enabled flag. */ + public static boolean enabled; + + public String user = "user"; + public String password = "password"; + + /** + * @param ctx Kernal context. + */ + protected TestAuthProcessor(GridKernalContext ctx) { + super(ctx); + } + + /** {@inheritDoc} */ + @Override public SecurityContext authenticateNode(ClusterNode node, + SecurityCredentials cred) throws IgniteCheckedException { + return new AllowAll(); + } + + /** {@inheritDoc} */ + @Override public boolean isGlobalNodeAuthentication() { + return false; + } + + /** {@inheritDoc} */ + @Override public SecurityContext authenticate(AuthenticationContext authCtx) throws IgniteCheckedException { + SecurityCredentials credentials = authCtx.credentials(); + if(credentials != null && user.equals(credentials.getLogin())) { + if(credentials.getPassword().toString().equals(password)) { + log.info("Admin access granted by " + authCtx.subjectType() + " from " + authCtx.address()); + return new AllowAll(); + } else { + log.warning("Admin password error by " + authCtx.subjectType() + " from " + authCtx.address()); + } + } + UUID uuid = authCtx.subjectId(); + SecuritySubjectType securitySubjectType = authCtx.subjectType(); + return new AllowSome( + new Subject( uuid, + securitySubjectType, + credentials != null ? credentials.getLogin() : null, + authCtx.address() ) + ); + } + + /** {@inheritDoc} */ + @Override public Collection authenticatedSubjects() throws IgniteCheckedException { + return Collections.emptyList(); + } + + /** {@inheritDoc} */ + @Override public SecuritySubject authenticatedSubject(UUID subjId) throws IgniteCheckedException { + return null; + } + + /** {@inheritDoc} */ + @Override public void authorize(String name, SecurityPermission perm, + @Nullable SecurityContext securityCtx) throws SecurityException { + if(securityCtx != null) { + if(AllowSome.class == securityCtx.getClass()) { + if(perm != SecurityPermission.TASK_EXECUTE) { + SecuritySubject s = securityCtx.subject(); + log.info("Access denied on " + + perm + + "@" + + name + + " by " + + s.type() + + " from " + + s.address() + + ". Login " + + s.login()); + throw new SecurityException("Access denied"); + } + } + } + } + + /** {@inheritDoc} */ + @Override public void onSessionExpired(UUID subjId) { + + } + + /** {@inheritDoc} */ + @Override public boolean enabled() { + return enabled; + } + + private static class AllowAll implements SecurityContext, Serializable { + /** + * Serial version uid. + */ + private static final long serialVersionUID = 0L; + + /** + * {@inheritDoc} + */ + @Override + public SecuritySubject subject() { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean taskOperationAllowed( String taskClsName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean cacheOperationAllowed( String cacheName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean serviceOperationAllowed( String srvcName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean systemOperationAllowed( SecurityPermission perm ) { + return true; + } + } + + private static class AllowSome implements SecurityContext, Serializable { + /** + * Serial version uid. + */ + private static final long serialVersionUID = 0L; + + private final SecuritySubject subject; + + AllowSome( SecuritySubject subject ) { + this.subject = subject; + } + + /** + * {@inheritDoc} + */ + @Override + public SecuritySubject subject() { + return subject; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean taskOperationAllowed( String taskClsName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean cacheOperationAllowed( String cacheName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean serviceOperationAllowed( String srvcName, SecurityPermission perm ) { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean systemOperationAllowed( SecurityPermission perm ) { + return true; + } + } + + private static class Subject implements SecuritySubject { + private static final long serialVersionUID = 0L; + private final UUID id; + private final SecuritySubjectType type; + private final Object login; + private final InetSocketAddress address; + + Subject( UUID id, SecuritySubjectType type, Object login, InetSocketAddress address ) { + this.id = id; + this.type = type; + this.login = login; + this.address = address; + } + + @Override + public UUID id() { + return id; + } + + @Override + public SecuritySubjectType type() { + return type; + } + + @Override + public Object login() { + return login; + } + + @Override + public InetSocketAddress address() { + return address; + } + + @Override + public SecurityPermissionSet permissions() { + return null; + } + } +}