Commit 645bc94e by Bolke de Bruin Committed by nixonrodrigues

ATLAS-3153 :- Add Keycloak authentication method to Atlas.

Keycloak is an open source Identity and Access Management solution aimed at modern applications and services. It makes it easy to secure applications and services with little to no code. This enabled Atlas to use OpenID Connect (OAUTH2) and allows integration with more services. Signed-off-by: 's avatarnixonrodrigues <nixon@apache.org>
parent 2c375b08
......@@ -7,6 +7,7 @@ Atlas supports following authentication methods
* *File*
* *Kerberos*
* *LDAP*
* *Keycloak (OpenID Connect / OAUTH2)*
Following properties should be set true to enable the authentication of that type in =atlas-application.properties= file.
......@@ -16,6 +17,7 @@ Following properties should be set true to enable the authentication of that typ
atlas.authentication.method.kerberos=true|false
atlas.authentication.method.ldap=true|false
atlas.authentication.method.file=true|false
atlas.authentication.method.keycloak=true|false
</verbatim>
If two or more authentication methods are set to true, then the authentication falls back to the latter method if the earlier one fails.
......@@ -111,3 +113,29 @@ atlas.authentication.method.ldap.user.searchfilter=(uid={0})
atlas.authentication.method.ldap.default.role=ROLE_USER
</verbatim>
---++++ Keycloak Method.
To enable Keycloak authentication mode in Atlas, set the property =atlas.authentication.method.keycloak= to true and also set the property =atlas.authentication.method.keycloak.file= to the localtion of your =keycloak.json= in =atlas-application.properties=.
Also set =atlas.authentication.method.keycloak.ugi-groups= to false if you want to pickup groups from Keycloak. By default the groups will be picked up from the *roles* defined in Keycloak. In case you want to use the groups
you need to create a mapping in keycloak and define =atlas.authentication.method.keycloak.groups_claim= equal to the token claim name. Make sure *not* to use the full group path and add the information to the access token.
<verbatim>
atlas.authentication.method.keycloak=true
atlas.authentication.method.keycloak.file=/opt/atlas/conf/keycloak.json
atlas.authentication.method.keycloak.ugi-groups=false
</verbatim>
Setup you keycloak.json per instructions from Keycloak. Make sure to include ="principal-attribute": "preferred_username"= to ensure readable user names and ="autodetect-bearer-only": true=.
<verbatim>
{
"realm": "auth",
"auth-server-url": "http://keycloak-server/auth",
"ssl-required": "external",
"resource": "atlas",
"public-client": true,
"confidential-port": 0,
"principal-attribute": "preferred_username",
"autodetect-bearer-only": true
}
</verbatim>
......@@ -719,6 +719,10 @@
<hppc.version>0.8.1</hppc.version>
<!-- Storm dependencies -->
<!-- keycloak dependencies -->
<keycloak.version>6.0.1</keycloak.version>
<!-- keycloak dependencies -->
<PermGen>64m</PermGen>
<MaxPermGen>512m</MaxPermGen>
......
......@@ -468,6 +468,14 @@
<artifactId>hadoop-aws</artifactId>
<version>${hadoop.version}</version>
</dependency>
<!-- Keycloak -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>${keycloak.version}</version>
</dependency>
</dependencies>
<build>
......
......@@ -37,11 +37,14 @@ public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProv
private boolean fileAuthenticationMethodEnabled = true;
private boolean pamAuthenticationEnabled = false;
private boolean keycloakAuthenticationEnabled = false;
private String ldapType = "NONE";
public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file";
public static final String LDAP_AUTH_METHOD = "atlas.authentication.method.ldap";
public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type";
public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam";
public static final String KEYCLOAK_AUTH_METHOD = "atlas.authentication.method.keycloak";
......@@ -55,15 +58,19 @@ public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProv
final AtlasPamAuthenticationProvider pamAuthenticationProvider;
final AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider;
@Inject
public AtlasAuthenticationProvider(AtlasLdapAuthenticationProvider ldapAuthenticationProvider,
AtlasFileAuthenticationProvider fileAuthenticationProvider,
AtlasADAuthenticationProvider adAuthenticationProvider,
AtlasPamAuthenticationProvider pamAuthenticationProvider) {
AtlasPamAuthenticationProvider pamAuthenticationProvider,
AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider) {
this.ldapAuthenticationProvider = ldapAuthenticationProvider;
this.fileAuthenticationProvider = fileAuthenticationProvider;
this.adAuthenticationProvider = adAuthenticationProvider;
this.pamAuthenticationProvider = pamAuthenticationProvider;
this.atlasKeycloakAuthenticationProvider = atlasKeycloakAuthenticationProvider;
}
@PostConstruct
......@@ -75,6 +82,8 @@ public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProv
this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false);
this.keycloakAuthenticationEnabled = configuration.getBoolean(KEYCLOAK_AUTH_METHOD, false);
boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false);
if (ldapAuthenticationEnabled) {
......@@ -118,6 +127,12 @@ public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProv
} catch (Exception ex) {
LOG.error("Error while PAM authentication", ex);
}
} else if (keycloakAuthenticationEnabled) {
try {
authentication = atlasKeycloakAuthenticationProvider.authenticate(authentication);
} catch (Exception ex) {
LOG.error("Error while Keycloak authentication", ex);
}
}
}
......@@ -137,6 +152,21 @@ public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProv
throw new AtlasAuthenticationException("Authentication failed.");
}
@Override
public boolean supports(Class<?> authentication) {
if (pamAuthenticationEnabled) {
return pamAuthenticationProvider.supports(authentication);
} else if (ldapType.equalsIgnoreCase("LDAP")) {
return ldapAuthenticationProvider.supports(authentication);
} else if (ldapType.equalsIgnoreCase("AD")) {
return adAuthenticationProvider.supports(authentication);
} else if (keycloakAuthenticationEnabled) {
return atlasKeycloakAuthenticationProvider.supports(authentication);
} else {
return super.supports(authentication);
}
}
public boolean isSsoEnabled() {
return ssoEnabled;
}
......
/*
* 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.atlas.web.security;
import org.apache.atlas.ApplicationProperties;
import org.apache.commons.configuration.Configuration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
public class AtlasKeycloakAuthenticationProvider extends AtlasAbstractAuthenticationProvider {
private final boolean groupsFromUGI;
private final String groupsClaim;
private final KeycloakAuthenticationProvider keycloakAuthenticationProvider;
public AtlasKeycloakAuthenticationProvider() throws Exception {
this.keycloakAuthenticationProvider = new KeycloakAuthenticationProvider();
Configuration configuration = ApplicationProperties.get();
this.groupsFromUGI = configuration.getBoolean("atlas.authentication.method.keycloak.ugi-groups", true);
this.groupsClaim = configuration.getString("atlas.authentication.method.keycloak.groups_claim");
}
@Override
public Authentication authenticate(Authentication authentication) {
authentication = keycloakAuthenticationProvider.authenticate(authentication);
if (groupsFromUGI) {
List<GrantedAuthority> groups = getAuthoritiesFromUGI(authentication.getName());
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
authentication = new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), groups);
} else if (groupsClaim != null) {
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken)authentication;
Map<String, Object> claims = token.getAccount().getKeycloakSecurityContext().getToken().getOtherClaims();
if (claims.containsKey(groupsClaim)) {
List<String> membership = (List<String>)claims.get(groupsClaim);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (String group : membership) {
grantedAuthorities.add(new SimpleGrantedAuthority(group));
}
authentication = new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), grantedAuthorities);
}
}
return authentication;
}
@Override
public boolean supports(Class<?> aClass) {
return keycloakAuthenticationProvider.supports(aClass);
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment