diff --git a/build-images.sh b/build-images.sh
index c41c8e9a47..6032b701e7 100755
--- a/build-images.sh
+++ b/build-images.sh
@@ -80,6 +80,8 @@ case $CLUSTER_TYPE in
docker build -f ./supports/tools/docker/multihost/kerberos/Dockerfile-kdc -t cloud-hub.adsw.io/library/ssm-kdc-server:${HADOOP_VERSION} .
+ docker build -f ./supports/tools/docker/multihost/ldap/Dockerfile-samba -t hub.adsw.io/qa-samba/samba:demo .
+
docker build -f ./supports/tools/docker/multihost/datanode/Dockerfile-hadoop-datanode -t cloud-hub.adsw.io/library/hadoop-datanode:${HADOOP_VERSION} .
docker build -f ./supports/tools/docker/multihost/namenode/Dockerfile-hadoop-namenode -t cloud-hub.adsw.io/library/hadoop-namenode:${HADOOP_VERSION} .
diff --git a/conf/smart-default.xml b/conf/smart-default.xml
index b95cdacbb6..198d01b2fe 100644
--- a/conf/smart-default.xml
+++ b/conf/smart-default.xml
@@ -648,4 +648,12 @@
PASSWORD_COMPARE: search user by specified filters and use LDAP password compare operation
+
+
+ smart.rest.server.auth.failures.logging.enabled
+ true
+
+ Whether to enable unsuccessful REST server auth attempts.
+
+
diff --git a/docs/rest-server-auth.md b/docs/rest-server-auth.md
index 8e55e6c430..7857197010 100644
--- a/docs/rest-server-auth.md
+++ b/docs/rest-server-auth.md
@@ -96,14 +96,14 @@ and `smart.rest.server.auth.ldap.user.search.groups`
Supported options:
-| Name | Default | Description |
-|--------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------|
-| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object |
-| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses |
-| smart.rest.server.auth.ldap.user.attributes.membership | memberOf | The group membership attribute of user LDAP object |
-| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass |
-| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object |
-| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate |
+| Name | Default | Description |
+|--------------------------------------------------------|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
+| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object |
+| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses |
+| smart.rest.server.auth.ldap.user.attributes.membership | memberOf (you should provide this value explicitly in smart-site.xml) | The group membership attribute of user LDAP object |
+| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass |
+| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object |
+| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate |
### Group member attribute strategy
@@ -120,14 +120,14 @@ and `smart.rest.server.auth.ldap.user.search.groups`
Supported options:
-| Name | Default | Description |
-|-----------------------------------------------------|--------------|------------------------------------------------------------------------------------------------|
-| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object |
-| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses |
-| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass |
-| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object |
-| smart.rest.server.auth.ldap.group.attributes.member | member | The member attribute of group LDAP object |
-| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate |
+| Name | Default | Description |
+|-----------------------------------------------------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
+| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object |
+| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses |
+| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass |
+| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object |
+| smart.rest.server.auth.ldap.group.attributes.member | member (you should provide this value explicitly in smart-site.xml) | The member attribute of group LDAP object |
+| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate |
### User name attribute strategy
diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
index ef6c502c60..3d673217c4 100644
--- a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
+++ b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
@@ -140,4 +140,7 @@ public class ConfigKeys {
public static final String SMART_REST_SERVER_LDAP_BIND_PASSWORD =
"smart.rest.server.auth.ldap.bind.password";
+
+ public static final String SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED =
+ "smart.rest.server.auth.failures.logging.enabled";
}
diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
index 476e51747a..32473a5d4d 100644
--- a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
+++ b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
@@ -18,6 +18,7 @@
package org.smartdata.server.config;
import org.smartdata.security.SmartPrincipalManager;
+import org.smartdata.server.error.AuthenticationFailureListener;
import org.smartdata.server.security.SmartPrincipalInitializerFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
@@ -33,13 +34,16 @@
import java.util.List;
import java.util.Set;
+import static org.smartdata.server.config.ConfigKeys.SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED;
+import static org.smartdata.server.config.ConfigKeys.WEB_SECURITY_ENABLED;
+
@Configuration
public class SecurityConfiguration {
private static final String SESSION_COOKIE_NAME = "SSM_SESSIONID";
private static final String API_ENDPOINTS_PATTERN = "/api/**";
@Bean
- @ConditionalOnProperty(name = ConfigKeys.WEB_SECURITY_ENABLED, havingValue = "true")
+ @ConditionalOnProperty(name = WEB_SECURITY_ENABLED, havingValue = "true")
public AuthenticationManager authenticationManager(
List authenticationProviders) {
if (authenticationProviders.isEmpty()) {
@@ -50,7 +54,16 @@ public AuthenticationManager authenticationManager(
}
@Bean
- @ConditionalOnProperty(name = ConfigKeys.WEB_SECURITY_ENABLED, havingValue = "true")
+ @ConditionalOnProperty(
+ name = {WEB_SECURITY_ENABLED, SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED},
+ havingValue = "true"
+ )
+ public AuthenticationFailureListener authenticationFailureListener() {
+ return new AuthenticationFailureListener();
+ }
+
+ @Bean
+ @ConditionalOnProperty(name = WEB_SECURITY_ENABLED, havingValue = "true")
public SecurityFilterChain securityFilterChain(
HttpSecurity http,
SmartPrincipalManager principalManager,
@@ -73,7 +86,7 @@ public SecurityFilterChain securityFilterChain(
@Bean
@ConditionalOnProperty(
- name = ConfigKeys.WEB_SECURITY_ENABLED,
+ name = WEB_SECURITY_ENABLED,
havingValue = "false",
matchIfMissing = true)
public SecurityFilterChain disabledSecurityFilterChain(HttpSecurity http) throws Exception {
diff --git a/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java b/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java
new file mode 100644
index 0000000000..82b9809642
--- /dev/null
+++ b/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java
@@ -0,0 +1,33 @@
+/**
+ * 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.smartdata.server.error;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationListener;
+import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
+
+@Slf4j
+public class AuthenticationFailureListener implements
+ ApplicationListener {
+
+ @Override
+ public void onApplicationEvent(AbstractAuthenticationFailureEvent event) {
+ log.error("Failed login attempt for {}",
+ event.getAuthentication().getName(), event.getException());
+ }
+}
diff --git a/supports/tools/docker/README.md b/supports/tools/docker/README.md
index c9b302bb0c..90dc56df0d 100644
--- a/supports/tools/docker/README.md
+++ b/supports/tools/docker/README.md
@@ -50,11 +50,13 @@ cd ./supports/tools/docker
Use one of the following credentials to log in to the Web UI
-| Login | Password | Type |
-|----------------|-----------|----------|
-| john | 1234 | static |
-| krb_user1@DEMO | krb_pass1 | kerberos |
-| krb_user2@DEMO | krb_pass2 | kerberos |
+| Login | Password | Type |
+|----------------|---------------|----------|
+| john | 1234 | static |
+| krb_user1@DEMO | krb_pass1 | kerberos |
+| krb_user2@DEMO | krb_pass2 | kerberos |
+| july | kitty_cat | ldap |
+| ben | bens_password | ldap |
### Testing SPNEGO auth
diff --git a/supports/tools/docker/multihost/conf/smart-default.xml b/supports/tools/docker/multihost/conf/smart-default.xml
index ac73f944c1..1493ea8ed0 100644
--- a/supports/tools/docker/multihost/conf/smart-default.xml
+++ b/supports/tools/docker/multihost/conf/smart-default.xml
@@ -544,4 +544,115 @@
Whether to enable SSL support for the SSM REST server.
+
+
+ smart.rest.server.auth.ldap.enabled
+ false
+
+ Whether to enable SSM REST server basic LDAP authentication method support.
+
+
+
+
+ smart.rest.server.auth.ldap.search.base
+
+
+ Base LDAP distinguished name for search.
+
+
+
+
+ smart.rest.server.auth.ldap.user.search.base
+
+
+ Base LDAP distinguished name for user search.
+
+
+
+
+ smart.rest.server.auth.ldap.group.search.base
+
+
+ Base LDAP distinguished name for group search.
+
+
+
+
+ smart.rest.server.auth.ldap.user.attributes.name
+ uid
+
+ The name attribute of user LDAP object.
+
+
+
+
+ smart.rest.server.auth.ldap.user.object-classes
+ person
+
+ Comma-separated list of LDAP user entry objectClasses.
+
+
+
+
+ smart.rest.server.auth.ldap.user.search.scope
+ ONE_LEVEL
+
+ The scope of LDAP user search. Possible values:
+ OBJECT - Search the named object
+ ONE_LEVEL - Search one level of the named context
+ SUBTREE - Search the entire subtree rooted at the named object
+
+
+
+
+ smart.rest.server.auth.ldap.group.search.scope
+ ONE_LEVEL
+
+ The scope of LDAP group search. Possible values:
+ OBJECT - Search the named object
+ ONE_LEVEL - Search one level of the named context
+ SUBTREE - Search the entire subtree rooted at the named object
+
+
+
+
+ smart.rest.server.auth.ldap.user.attributes.password
+ userPassword
+
+ The password attribute of user LDAP object.
+
+
+
+
+ smart.rest.server.auth.ldap.group.object-class
+ groupOfNames
+
+ LDAP group entry objectClass.
+
+
+
+
+ smart.rest.server.auth.ldap.group.attributes.name
+ cn
+
+ The name attribute of group LDAP object.
+
+
+
+
+ smart.rest.server.auth.ldap.auth.type
+ BIND
+
+ LDAP authentication type. Possible values:
+ BIND: search user by specified filters and authenticate with found user's DN and provided password
+ PASSWORD_COMPARE: search user by specified filters and use LDAP password compare operation
+
+
+
+ smart.rest.server.auth.failures.logging.enabled
+ true
+
+ Whether to enable unsuccessful REST server auth attempts.
+
+
diff --git a/supports/tools/docker/multihost/conf/smart-site.xml b/supports/tools/docker/multihost/conf/smart-site.xml
index 1cad7ef24e..f834968d31 100644
--- a/supports/tools/docker/multihost/conf/smart-site.xml
+++ b/supports/tools/docker/multihost/conf/smart-site.xml
@@ -91,4 +91,56 @@
smart.rest.server.auth.spnego.principal
HTTP/ssm-server.demo@DEMO
+
+ smart.rest.server.auth.ldap.enabled
+ true
+
+
+ smart.rest.server.auth.ldap.url
+ ldap://samba:389
+
+
+ smart.rest.server.auth.ldap.search.base
+ dc=ssm,dc=test
+
+
+ smart.rest.server.auth.ldap.user.search.base
+ ou=people,dc=ssm,dc=test
+
+
+ smart.rest.server.auth.ldap.group.search.base
+ ou=groups,dc=ssm,dc=test
+
+
+ smart.rest.server.auth.ldap.user.attributes.name
+ sAMAccountName
+
+
+ smart.rest.server.auth.ldap.user.search.scope
+ SUBTREE
+
+
+ smart.rest.server.auth.ldap.group.search.scope
+ SUBTREE
+
+
+ smart.rest.server.auth.ldap.auth.type
+ BIND
+
+
+ smart.rest.server.auth.ldap.bind.user
+ cn=Administrator,CN=Users,DC=ssm,DC=test
+
+
+ smart.rest.server.auth.ldap.bind.password
+ LZ2ibGAn2H0D0UW3
+
+
+ smart.rest.server.auth.ldap.user.attributes.membership
+ memberOf
+
+
+ smart.rest.server.auth.ldap.user.search.groups
+ developers
+
\ No newline at end of file
diff --git a/supports/tools/docker/multihost/docker-compose.yaml b/supports/tools/docker/multihost/docker-compose.yaml
index b4b3a31919..8f03cc63e0 100644
--- a/supports/tools/docker/multihost/docker-compose.yaml
+++ b/supports/tools/docker/multihost/docker-compose.yaml
@@ -110,6 +110,20 @@ services:
networks:
- demo
+ samba:
+ image: hub.adsw.io/qa-samba/samba:demo
+ hostname: samba
+ container_name: samba
+ privileged: true
+ ports:
+ - "389:389"
+ environment:
+ SMB_ADMIN_PASSWORD: "LZ2ibGAn2H0D0UW3"
+ volumes:
+ - ./ldap:/opt/ad-scripts
+ networks:
+ - demo
+
networks:
demo:
name: demo
diff --git a/supports/tools/docker/multihost/ldap/Dockerfile-samba b/supports/tools/docker/multihost/ldap/Dockerfile-samba
new file mode 100644
index 0000000000..f9f0b0eb5f
--- /dev/null
+++ b/supports/tools/docker/multihost/ldap/Dockerfile-samba
@@ -0,0 +1,4 @@
+FROM hub.adsw.io/library/adcc-test-ubuntu-base:latest-x64
+RUN mkdir -p /opt/ad-scripts
+WORKDIR /opt/ad-scripts
+CMD chmod +x *.sh && ./samba-ad-setup.sh && ./samba-ad-run.sh
diff --git a/supports/tools/docker/multihost/ldap/people.ldif b/supports/tools/docker/multihost/ldap/people.ldif
new file mode 100644
index 0000000000..94921233a1
--- /dev/null
+++ b/supports/tools/docker/multihost/ldap/people.ldif
@@ -0,0 +1,61 @@
+dn: ou=groups,dc=ssm,dc=test
+objectclass: top
+objectclass: organizationalUnit
+ou: groups
+
+dn: ou=people,dc=ssm,dc=test
+objectclass: top
+objectclass: organizationalUnit
+ou: people
+
+dn: cn=july,ou=people,dc=ssm,dc=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: july
+sn: july
+givenName: july
+description: kitty_cat pwd
+unicodePwd::IgBrAGkAdAB0AHkAXwBjAGEAdAAiAA==
+userAccountControl: 512
+sAMAccountName: july
+
+dn: cn=ben,ou=people,dc=ssm,dc=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: ben
+sn: ben
+givenName: ben
+description: bens_password pwd
+unicodePwd::IgBiAGUAbgBzAF8AcABhAHMAcwB3AG8AcgBkACIA
+userAccountControl: 512
+sAMAccountName: ben
+
+dn: cn=bob,ou=people,dc=ssm,dc=test
+objectClass: top
+objectClass: person
+objectClass: organizationalPerson
+objectClass: user
+cn: bob
+sn: bob
+givenName: bob
+description: b0bs_p4ssw0rd pwd
+unicodePwd::IgBiADAAYgBzAF8AcAA0AHMAcwB3ADAAcgBkACIA
+userAccountControl: 512
+sAMAccountName: bob
+
+dn: cn=developers,ou=groups,dc=ssm,dc=test
+objectclass: top
+objectclass: groupOfNames
+cn: developers
+member: cn=july,ou=people,dc=ssm,dc=test
+member: cn=ben,ou=people,dc=ssm,dc=test
+
+dn: cn=managers,ou=groups,dc=ssm,dc=test
+objectclass: top
+objectclass: groupOfNames
+cn: managers
+member: cn=bob,ou=people,dc=ssm,dc=test
diff --git a/supports/tools/docker/multihost/ldap/samba-ad-run.sh b/supports/tools/docker/multihost/ldap/samba-ad-run.sh
new file mode 100755
index 0000000000..1de95fecfb
--- /dev/null
+++ b/supports/tools/docker/multihost/ldap/samba-ad-run.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+set -e
+
+[ -f /var/lib/samba/.setup ] || {
+ >&2 echo "[ERROR] Samba is not setup yet, which should happen automatically. Look for errors!"
+ exit 127
+}
+
+samba -i -s /var/lib/samba/private/smb.conf &
+
+# Update the password policy settings
+samba-tool domain passwordsettings set --complexity=off
+samba-tool domain passwordsettings set --min-pwd-length=4
+samba-tool domain passwordsettings set --history-length=0
+
+sleep 10
+
+set +e
+ldapadd -x -H ldap://samba:389 -D "cn=Administrator,CN=Users,DC=ssm,DC=test" -w "$SMB_ADMIN_PASSWORD" -f /opt/ad-scripts/people.ldif
+LDAPADD_STATUS=$?
+set -e
+
+if [ $LDAPADD_STATUS -ne 0 ]; then
+ >&2 echo "[ERROR] ldapadd failed with exit code $LDAPADD_STATUS"
+fi
+
+tail -f /dev/null
diff --git a/supports/tools/docker/multihost/ldap/samba-ad-setup.sh b/supports/tools/docker/multihost/ldap/samba-ad-setup.sh
new file mode 100755
index 0000000000..666a4a06d3
--- /dev/null
+++ b/supports/tools/docker/multihost/ldap/samba-ad-setup.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+set -e
+
+info () {
+ echo "[INFO] $@"
+}
+
+info "Running setup"
+
+[ -f /var/lib/samba/.setup ] && info "Already setup..." && exit 0
+
+info "Provisioning domain controller..."
+
+info "Given admin password: ${SMB_ADMIN_PASSWORD}"
+
+rm -f /etc/samba/smb.conf
+
+samba-tool domain provision\
+ --server-role=dc\
+ --use-rfc2307\
+ --dns-backend=SAMBA_INTERNAL\
+ --realm=ssm.test\
+ --domain=test\
+ --adminpass=${SMB_ADMIN_PASSWORD}
+
+sed -i '/^\[global\]/a client ldap sasl wrapping = sign\nldap server require strong auth = no\n' /etc/samba/smb.conf
+sed -i '/^\[global\]/a client ldap sasl wrapping = sign\nldap server require strong auth = no\n' /etc/samba/smb.conf
+
+cp /etc/samba/smb.conf /var/lib/samba/private/smb.conf
+rm -f /etc/samba/smb.conf
+
+touch /var/lib/samba/.setup