mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-931 ldap identity provider (#2609)
* CB-931 ldap identity provider * CB-931 add bruteforce protection to ldap --------- Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
co-authored by
Evgenia Bezborodova
parent
7cd22a84c2
commit
72ce16b2e8
@@ -22,6 +22,7 @@ Require-Bundle: org.jkiss.dbeaver.data.gis;visibility:=reexport,
|
||||
Export-Package: io.cloudbeaver,
|
||||
io.cloudbeaver.auth,
|
||||
io.cloudbeaver.auth.provider,
|
||||
io.cloudbeaver.auth.provider.fa,
|
||||
io.cloudbeaver.auth.provider.local,
|
||||
io.cloudbeaver.auth.provisioning,
|
||||
io.cloudbeaver.websocket,
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed 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 io.cloudbeaver.auth.provider.fa;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.model.auth.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractSessionExternal implements SMSessionExternal {
|
||||
|
||||
@NotNull
|
||||
protected final Map<String, Object> authParameters;
|
||||
@NotNull
|
||||
protected final SMSession parentSession;
|
||||
@NotNull
|
||||
protected final SMAuthSpace space;
|
||||
|
||||
protected AbstractSessionExternal(
|
||||
@NotNull SMSession parentSession,
|
||||
@NotNull SMAuthSpace space,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) {
|
||||
this.parentSession = parentSession;
|
||||
this.space = space;
|
||||
this.authParameters = authParameters;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMAuthSpace getSessionSpace() {
|
||||
return space;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SMSessionContext getSessionContext() {
|
||||
return this.parentSession.getSessionContext();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SMSessionPrincipal getSessionPrincipal() {
|
||||
return parentSession.getSessionPrincipal();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LocalDateTime getSessionStart() {
|
||||
return parentSession.getSessionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAuthParameters() {
|
||||
return authParameters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Vendor: Cloudbeaver LDAP
|
||||
Bundle-Vendor: DBeaver Corp
|
||||
Bundle-SymbolicName: io.cloudbeaver.service.ldap.auth;singleton:=true
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Release-Date: 20240506
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-17
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-ClassPath: .
|
||||
Require-Bundle: org.jkiss.dbeaver.model;visibility:=reexport,
|
||||
org.jkiss.dbeaver.registry;visibility:=reexport,
|
||||
io.cloudbeaver.model
|
||||
Automatic-Module-Name: io.cloudbeaver.service.ldap.auth
|
||||
@@ -0,0 +1,5 @@
|
||||
source.. = src/
|
||||
output.. = target/classes/
|
||||
bin.includes = .,\
|
||||
META-INF/,\
|
||||
plugin.xml
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?eclipse version="3.2"?>
|
||||
|
||||
<plugin>
|
||||
<extension point="org.jkiss.dbeaver.auth.provider">
|
||||
<authProvider id="ldap" label="LDAP" description="LDAP authentication provider"
|
||||
configurable="true"
|
||||
class="io.cloudbeaver.service.ldap.auth.LdapAuthProvider"
|
||||
icon="platform:/plugin/org.jkiss.dbeaver.model/icons/idp/ldap.png"
|
||||
>
|
||||
<configuration>
|
||||
<propertyGroup label="LDAP" description="LDAP authentication">
|
||||
<property id="ldap-host" label="Host" type="string" description="LDAP server host" required="true"/>
|
||||
<property id="ldap-port" label="Port" type="integer" defaultValue="389" required="true" description="LDAP server port, default is 389"/>
|
||||
<property id="ldap-dn" label="Base Distinguished Name" type="string"
|
||||
description="Base Distinguished Name applicable for all users, example: dc=myOrg,dc=com"
|
||||
required="true"
|
||||
/>
|
||||
</propertyGroup>
|
||||
</configuration>
|
||||
<credentials>
|
||||
<propertyGroup label="Auth credentials">
|
||||
<property id="units" label="Unit paths" type="string" description="LDAP unit paths separated by commas. Example ou=unit1,ou=unit2" user="true"/>
|
||||
<property id="user" label="User name" type="string" description="LDAP user name" user="true"/>
|
||||
<property id="password" label="User password" type="string" description="LDAP user password"
|
||||
user="true" encryption="plain"/>
|
||||
</propertyGroup>
|
||||
</credentials>
|
||||
</authProvider>
|
||||
</extension>
|
||||
</plugin>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>io.cloudbeaver</groupId>
|
||||
<artifactId>bundles</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
<artifactId>io.cloudbeaver.service.ldap.auth</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
</project>
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed 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 io.cloudbeaver.service.ldap.auth;
|
||||
|
||||
import io.cloudbeaver.DBWUserIdentity;
|
||||
import io.cloudbeaver.auth.SMAuthProviderExternal;
|
||||
import io.cloudbeaver.auth.SMBruteForceProtected;
|
||||
import io.cloudbeaver.auth.provider.local.LocalAuthProviderConstants;
|
||||
import io.cloudbeaver.model.session.WebSession;
|
||||
import io.cloudbeaver.model.user.WebUser;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.code.Nullable;
|
||||
import org.jkiss.dbeaver.DBException;
|
||||
import org.jkiss.dbeaver.model.DBPObject;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.dbeaver.model.security.SMController;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.directory.DirContext;
|
||||
import javax.naming.directory.InitialDirContext;
|
||||
import java.util.HashMap;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LdapAuthProvider implements SMAuthProviderExternal<SMSession>, SMBruteForceProtected {
|
||||
public LdapAuthProvider() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> authExternalUser(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@Nullable SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) throws DBException {
|
||||
if (providerConfig == null) {
|
||||
throw new DBException("LDAP provider config is null");
|
||||
}
|
||||
String userName = JSONUtils.getString(authParameters, LdapConstants.CRED_USERNAME);
|
||||
if (CommonUtils.isEmpty(userName)) {
|
||||
throw new DBException("LDAP user name is empty");
|
||||
}
|
||||
String password = JSONUtils.getString(authParameters, LdapConstants.CRED_PASSWORD);
|
||||
if (CommonUtils.isEmpty(password)) {
|
||||
throw new DBException("LDAP password is empty");
|
||||
}
|
||||
String unit = CommonUtils.nullIfEmpty(JSONUtils.getString(authParameters, LdapConstants.CRED_UNITS));
|
||||
|
||||
LdapSettings ldapSettings = new LdapSettings(providerConfig);
|
||||
Hashtable<String, String> environment = new Hashtable<>();
|
||||
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||
|
||||
var ldapProviderUrl = "ldap://" + ldapSettings.getHost() + ":" + ldapSettings.getPort();
|
||||
environment.put(Context.PROVIDER_URL, ldapProviderUrl);
|
||||
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||
|
||||
String cn = "cn=" + userName;
|
||||
var principal = Stream.of(cn, unit, ldapSettings.getBaseDN())
|
||||
.filter(CommonUtils::isNotEmpty)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
environment.put(Context.SECURITY_PRINCIPAL, principal);
|
||||
environment.put(Context.SECURITY_CREDENTIALS, password);
|
||||
try {
|
||||
DirContext context = new InitialDirContext(environment);
|
||||
context.close();
|
||||
Map<String, Object> userData = new HashMap<>();
|
||||
userData.put(LdapConstants.CRED_USERNAME, userName);
|
||||
userData.put(LdapConstants.CRED_SESSION_ID, UUID.randomUUID());
|
||||
return userData;
|
||||
} catch (Exception e) {
|
||||
throw new DBException("LDAP authentication failed: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DBWUserIdentity getUserIdentity(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) throws DBException {
|
||||
String userName = JSONUtils.getString(authParameters, LocalAuthProviderConstants.CRED_USER);
|
||||
if (CommonUtils.isEmpty(userName)) {
|
||||
throw new DBException("LDAP user name is empty");
|
||||
}
|
||||
return new DBWUserIdentity(userName, userName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public DBPObject getUserDetails(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull WebSession webSession,
|
||||
@NotNull SMSession session,
|
||||
@NotNull WebUser user,
|
||||
boolean selfIdentity
|
||||
) throws DBException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String validateLocalAuth(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMController securityController,
|
||||
@NotNull SMAuthProviderCustomConfiguration providerConfig,
|
||||
@NotNull Map<String, Object> userCredentials,
|
||||
@Nullable String activeUserId
|
||||
) throws DBException {
|
||||
String userId = JSONUtils.getString(userCredentials, LdapConstants.CRED_USERNAME);
|
||||
if (CommonUtils.isEmpty(userId)) {
|
||||
throw new DBException("LDAP user id not found");
|
||||
}
|
||||
return activeUserId == null ? userId : activeUserId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SMSession openSession(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMSession mainSession,
|
||||
@Nullable SMAuthProviderCustomConfiguration customConfiguration,
|
||||
@NotNull Map<String, Object> userCredentials
|
||||
) throws DBException {
|
||||
return new LdapSession(mainSession, mainSession.getSessionSpace(), userCredentials);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeSession(@NotNull SMSession mainSession, SMSession session) throws DBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshSession(
|
||||
@NotNull DBRProgressMonitor monitor,
|
||||
@NotNull SMSession mainSession,
|
||||
SMSession session
|
||||
) throws DBException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getInputUsername(@NotNull Map<String, Object> cred) {
|
||||
return cred.get(LdapConstants.CRED_USERNAME);
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed 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 io.cloudbeaver.service.ldap.auth;
|
||||
|
||||
public interface LdapConstants {
|
||||
String PARAM_HOST = "ldap-host";
|
||||
String PARAM_PORT = "ldap-port";
|
||||
String PARAM_DN = "ldap-dn";
|
||||
|
||||
|
||||
String CRED_USERNAME = "user";
|
||||
String CRED_PASSWORD = "password";
|
||||
String CRED_UNITS = "units";
|
||||
String CRED_SESSION_ID = "session-id";
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed 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 io.cloudbeaver.service.ldap.auth;
|
||||
|
||||
import io.cloudbeaver.auth.provider.fa.AbstractSessionExternal;
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.auth.SMAuthSpace;
|
||||
import org.jkiss.dbeaver.model.auth.SMSession;
|
||||
import org.jkiss.dbeaver.model.data.json.JSONUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class LdapSession extends AbstractSessionExternal {
|
||||
protected LdapSession(
|
||||
@NotNull SMSession parentSession,
|
||||
@NotNull SMAuthSpace space,
|
||||
@NotNull Map<String, Object> authParameters
|
||||
) {
|
||||
super(parentSession, space, authParameters);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getSessionId() {
|
||||
return JSONUtils.getString(authParameters, LdapConstants.CRED_SESSION_ID, "sessionNotFound");
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* DBeaver - Universal Database Manager
|
||||
* Copyright (C) 2010-2024 DBeaver Corp and others
|
||||
*
|
||||
* Licensed 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 io.cloudbeaver.service.ldap.auth;
|
||||
|
||||
import org.jkiss.code.NotNull;
|
||||
import org.jkiss.dbeaver.model.security.SMAuthProviderCustomConfiguration;
|
||||
import org.jkiss.utils.CommonUtils;
|
||||
|
||||
public class LdapSettings {
|
||||
@NotNull
|
||||
private final SMAuthProviderCustomConfiguration providerConfiguration;
|
||||
@NotNull
|
||||
private final String host;
|
||||
@NotNull
|
||||
private final String baseDN;
|
||||
private final int port;
|
||||
|
||||
protected LdapSettings(SMAuthProviderCustomConfiguration providerConfiguration) {
|
||||
this.providerConfiguration = providerConfiguration;
|
||||
this.host = providerConfiguration.getParameter(LdapConstants.PARAM_HOST);
|
||||
this.port = CommonUtils.isNotEmpty(providerConfiguration.getParameter(LdapConstants.PARAM_PORT)) ? Integer.parseInt(
|
||||
providerConfiguration.getParameter(LdapConstants.PARAM_PORT)) : 389;
|
||||
this.baseDN = providerConfiguration.getParameterOrDefault(LdapConstants.PARAM_DN, "");
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public String getBaseDN() {
|
||||
return baseDN;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,9 @@
|
||||
<module>io.cloudbeaver.service.rm</module>
|
||||
<module>io.cloudbeaver.service.rm.nio</module>
|
||||
<module>io.cloudbeaver.service.data.transfer</module>
|
||||
|
||||
<module>io.cloudbeaver.service.security</module>
|
||||
<module>io.cloudbeaver.service.ldap.auth</module>
|
||||
|
||||
<module>io.cloudbeaver.resources.drivers.base</module>
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
<plugin id="io.cloudbeaver.service.fs" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
<plugin id="io.cloudbeaver.service.rm" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
<plugin id="io.cloudbeaver.service.rm.nio" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
|
||||
<plugin id="io.cloudbeaver.service.security" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
<plugin id="io.cloudbeaver.service.ldap.auth" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
|
||||
<!-- Extra plugins -->
|
||||
<plugin id="org.jkiss.dbeaver.data.office" download-size="0" install-size="0" version="0.0.0" unpack="false"/>
|
||||
|
||||
Reference in New Issue
Block a user