/* * Copyright 2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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 org.keycloak.example.kerberos; import org.ietf.jgss.GSSCredential; import org.keycloak.KeycloakPrincipal; import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.util.KerberosSerializationUtils; import org.keycloak.representations.AccessToken; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.InitialDirContext; import javax.security.sasl.Sasl; import javax.servlet.http.HttpServletRequest; import java.util.Hashtable; /** * Sample client able to authenticate against ApacheDS LDAP server with Krb5 GSS Credential. * * Credential was previously retrieved from SPNEGO authentication against Keycloak auth-server and transmitted from * Keycloak to the application in OIDC access token * * We can use GSSCredential to further GSS API calls . Note that if you will use GSS API directly, you can * attach GSSCredential when creating GSSContext like this: * GSSContext context = gssManager.createContext(serviceName, KerberosSerializationUtils.KRB5_OID, deserializedGssCredential, GSSContext.DEFAULT_LIFETIME); * * In this example we authenticate against LDAP server, which calls GSS API under the hood when credential is attached to env under Sasl.CREDENTIALS key * * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> */ public class GSSCredentialsClient { public static LDAPUser getUserFromLDAP(HttpServletRequest req) throws Exception { KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) req.getUserPrincipal(); AccessToken accessToken = keycloakPrincipal.getKeycloakSecurityContext().getToken(); String username = accessToken.getPreferredUsername(); // Retrieve kerberos credential from accessToken and deserialize it String serializedGssCredential = (String) accessToken.getOtherClaims().get(KerberosConstants.GSS_DELEGATION_CREDENTIAL); GSSCredential deserializedGssCredential = KerberosSerializationUtils.deserializeCredential(serializedGssCredential); // First try to invoke without gssCredential. It should fail. This is here just for illustration purposes try { invokeLdap(null, username); throw new RuntimeException("Not expected to authenticate to LDAP without credential"); } catch (NamingException nse) { System.out.println("GSSCredentialsClient: Expected exception: " + nse.getMessage()); } return invokeLdap(deserializedGssCredential, username); } private static LDAPUser invokeLdap(GSSCredential gssCredential, String username) throws NamingException { Hashtable env = new Hashtable(11); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:10389"); if (gssCredential != null) { env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI"); env.put(Sasl.CREDENTIALS, gssCredential); } DirContext ctx = new InitialDirContext(env); try { Attributes attrs = ctx.getAttributes("uid=" + username + ",ou=People,dc=keycloak,dc=org"); String uid = username; String cn = (String) attrs.get("cn").get(); String sn = (String) attrs.get("sn").get(); return new LDAPUser(uid, cn, sn); } finally { ctx.close(); } } public static class LDAPUser { private final String uid; private final String cn; private final String sn; public LDAPUser(String uid, String cn, String sn) { this.uid = uid; this.cn = cn; this.sn = sn; } public String getUid() { return uid; } public String getCn() { return cn; } public String getSn() { return sn; } } }