| /** |
| * 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.kerby.kerberos.kdc.identitybackend; |
| |
| import org.apache.directory.api.ldap.model.cursor.CursorException; |
| import org.apache.directory.api.ldap.model.cursor.EntryCursor; |
| import org.apache.directory.api.ldap.model.entry.DefaultEntry; |
| import org.apache.directory.api.ldap.model.entry.Entry; |
| import org.apache.directory.api.ldap.model.exception.LdapException; |
| import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; |
| import org.apache.directory.api.ldap.model.message.ModifyRequest; |
| import org.apache.directory.api.ldap.model.message.ModifyRequestImpl; |
| import org.apache.directory.api.ldap.model.message.SearchScope; |
| import org.apache.directory.api.ldap.model.name.Dn; |
| import org.apache.directory.api.ldap.model.name.Rdn; |
| import org.apache.directory.api.util.GeneralizedTime; |
| import org.apache.directory.ldap.client.api.LdapConnection; |
| import org.apache.directory.ldap.client.api.LdapNetworkConnection; |
| import org.apache.directory.shared.kerberos.KerberosAttribute; |
| import org.apache.kerby.config.Config; |
| import org.apache.kerby.kerberos.kerb.KrbException; |
| import org.apache.kerby.kerberos.kerb.identity.KrbIdentity; |
| import org.apache.kerby.kerberos.kerb.identity.backend.AbstractIdentityBackend; |
| import org.apache.kerby.kerberos.kerb.type.KerberosTime; |
| import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey; |
| import org.apache.kerby.kerberos.kerb.type.base.EncryptionType; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.text.ParseException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * An LDAP based backend implementation. |
| */ |
| public class LdapIdentityBackend extends AbstractIdentityBackend { |
| //The LdapConnection, may be LdapNetworkConnection or LdapCoreSessionConnection |
| private LdapConnection connection; |
| //This is used as a flag to represent the connection whether is |
| // LdapNetworkConnection object or not |
| private boolean isLdapNetworkConnection; |
| private static final Logger LOG = LoggerFactory.getLogger(LdapIdentityBackend.class); |
| |
| public LdapIdentityBackend() { |
| this.isLdapNetworkConnection = true; |
| } |
| |
| /** |
| * Constructing an instance using specified config that contains anything |
| * to be used to initialize an LdapConnection and necessary baseDn. |
| * @param config . The config is used to config the backend. |
| */ |
| public LdapIdentityBackend(Config config) { |
| setConfig(config); |
| this.isLdapNetworkConnection = true; |
| } |
| |
| /** |
| * Constructing an instance using a LdapConnection and a specified config |
| * that contains anything to be used to initialize a necessary baseDn. |
| * @param config The config is used to config the backend |
| * @param connection The connection to be used to handle the operations, |
| * may be a LdapNetworkConnection or a LdapCoreSessionConnection. |
| */ |
| public LdapIdentityBackend(Config config, |
| LdapConnection connection) { |
| setConfig(config); |
| this.connection = connection; |
| } |
| |
| /** |
| * Start the connection for the initialize() |
| */ |
| private void startConnection() throws LdapException { |
| if (isLdapNetworkConnection == true) { |
| this.connection = new LdapNetworkConnection(getConfig().getString("host"), |
| getConfig().getInt("port")); |
| } |
| connection.bind(getConfig().getString("admin_dn"), |
| getConfig().getString("admin_pw")); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void doInitialize() throws KrbException { |
| LOG.info("Initializing the Ldap identity backend."); |
| try { |
| startConnection(); |
| } catch (LdapException e) { |
| LOG.error("Failed to start connection with LDAP", e); |
| throw new KrbException("Failed to start connection with LDAP", e); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void doStop() throws KrbException { |
| try { |
| closeConnection(); |
| } catch (IOException e) { |
| LOG.error("Failed to close connection with LDAP", e); |
| throw new KrbException("Failed to close connection with LDAP", e); |
| } |
| LOG.info("closed connection with LDAP."); |
| } |
| |
| /** |
| * Close the connection for stop() |
| */ |
| private void closeConnection() throws IOException { |
| if (connection.isConnected()) { |
| connection.close(); |
| } |
| } |
| |
| /** |
| * Convert a KerberosTime type obeject to a generalized time form of String |
| * @param kerberosTime The kerberostime to convert |
| */ |
| private String toGeneralizedTime(KerberosTime kerberosTime) { |
| GeneralizedTime generalizedTime = new GeneralizedTime(kerberosTime.getValue()); |
| return generalizedTime.toString(); |
| } |
| |
| /** |
| * An inner class, used to encapsulate key information |
| */ |
| static class KeysInfo { |
| private String[] etypes; |
| private byte[][] keys; |
| private String[] kvnos; |
| |
| KeysInfo(KrbIdentity identity) throws KrbException { |
| Map<EncryptionType, EncryptionKey> keymap = identity.getKeys(); |
| this.etypes = new String[keymap.size()]; |
| this.keys = new byte[keymap.size()][]; |
| this.kvnos = new String[keymap.size()]; |
| int i = 0; |
| for (Map.Entry<EncryptionType, EncryptionKey> entryKey : keymap.entrySet()) { |
| etypes[i] = entryKey.getKey().getValue() + ""; |
| try { |
| keys[i] = entryKey.getValue().encode(); |
| } catch (IOException e) { |
| throw new KrbException("encode key failed", e); |
| } |
| kvnos[i] = entryKey.getValue().getKvno() + ""; |
| i++; |
| } |
| } |
| |
| public String[] getEtypes() { |
| return etypes; |
| } |
| |
| public byte[][] getKeys() { |
| return keys; |
| } |
| |
| public String[] getKvnos() { |
| return kvnos; |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected KrbIdentity doAddIdentity(KrbIdentity identity) throws KrbException { |
| String principalName = identity.getPrincipalName(); |
| String[] names = principalName.split("@"); |
| Entry entry = new DefaultEntry(); |
| KeysInfo keysInfo = new KeysInfo(identity); |
| try { |
| Dn dn = toDn(principalName); |
| entry.setDn(dn); |
| entry.add("objectClass", "top", "person", "inetOrgPerson", |
| "krb5principal", "krb5kdcentry"); |
| entry.add("cn", names[0]); |
| entry.add("sn", names[0]); |
| entry.add(KerberosAttribute.KRB5_KEY_AT, keysInfo.getKeys()); |
| entry.add("krb5EncryptionType", keysInfo.getEtypes()); |
| entry.add(KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principalName); |
| entry.add(KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, |
| identity.getKeyVersion() + ""); |
| entry.add("krb5KDCFlags", "" + identity.getKdcFlags()); |
| entry.add(KerberosAttribute.KRB5_ACCOUNT_DISABLED_AT, "" |
| + identity.isDisabled()); |
| entry.add("createTimestamp", |
| toGeneralizedTime(identity.getCreatedTime())); |
| entry.add(KerberosAttribute.KRB5_ACCOUNT_LOCKEDOUT_AT, "" |
| + identity.isLocked()); |
| entry.add(KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT, |
| toGeneralizedTime(identity.getExpireTime())); |
| connection.add(entry); |
| } catch (LdapInvalidDnException e) { |
| LOG.error("Error occurred while adding identity", e); |
| throw new KrbException("Failed to add identity", e); |
| } catch (LdapException e) { |
| LOG.error("Error occurred while adding identity", e); |
| throw new KrbException("Failed to add identity", e); |
| } |
| return getIdentity(principalName); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected KrbIdentity doGetIdentity(String principalName) throws KrbException { |
| KrbIdentity krbIdentity = new KrbIdentity(principalName); |
| try { |
| Dn dn = toDn(principalName); |
| Entry entry = connection.lookup(dn, "*", "+"); |
| if (entry == null) { |
| return null; |
| } |
| LdapIdentityGetHelper getHelper = new LdapIdentityGetHelper(entry); |
| krbIdentity.setPrincipal(getHelper.getPrincipalName()); |
| krbIdentity.setKeyVersion(getHelper.getKeyVersion()); |
| krbIdentity.addKeys(getHelper.getKeys()); |
| krbIdentity.setCreatedTime(getHelper.getCreatedTime()); |
| krbIdentity.setExpireTime(getHelper.getExpireTime()); |
| krbIdentity.setDisabled(getHelper.getDisabled()); |
| krbIdentity.setKdcFlags(getHelper.getKdcFlags()); |
| krbIdentity.setLocked(getHelper.getLocked()); |
| } catch (LdapException e) { |
| throw new KrbException("Failed to retrieve identity", e); |
| } catch (ParseException e) { |
| throw new KrbException("Failed to retrieve identity", e); |
| } catch (IOException e) { |
| throw new KrbException("Failed to retrieve identity", e); |
| } |
| |
| return krbIdentity; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected KrbIdentity doUpdateIdentity(KrbIdentity identity) throws KrbException { |
| String principalName = identity.getPrincipalName(); |
| KeysInfo keysInfo = new KeysInfo(identity); |
| try { |
| Dn dn = toDn(principalName); |
| ModifyRequest modifyRequest = new ModifyRequestImpl(); |
| modifyRequest.setName(dn); |
| modifyRequest.replace(KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, |
| "" + identity.getKeyVersion()); |
| modifyRequest.replace(KerberosAttribute.KRB5_KEY_AT, keysInfo.getKeys()); |
| modifyRequest.replace("krb5EncryptionType", keysInfo.getEtypes()); |
| modifyRequest.replace(KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, |
| identity.getPrincipalName()); |
| modifyRequest.replace(KerberosAttribute.KRB5_ACCOUNT_EXPIRATION_TIME_AT, |
| toGeneralizedTime(identity.getExpireTime())); |
| modifyRequest.replace(KerberosAttribute.KRB5_ACCOUNT_DISABLED_AT, "" |
| + identity.isDisabled()); |
| modifyRequest.replace("krb5KDCFlags", "" + identity.getKdcFlags()); |
| modifyRequest.replace(KerberosAttribute.KRB5_ACCOUNT_LOCKEDOUT_AT, "" |
| + identity.isLocked()); |
| connection.modify(modifyRequest); |
| } catch (LdapException e) { |
| LOG.error("Error occurred while updating identity: " + principalName, e); |
| throw new KrbException("Failed to update identity", e); |
| } |
| |
| return getIdentity(principalName); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void doDeleteIdentity(String principalName) throws KrbException { |
| try { |
| Dn dn = toDn(principalName); |
| connection.delete(dn); |
| } catch (LdapException e) { |
| LOG.error("Error occurred while deleting identity: " + principalName); |
| throw new KrbException("Failed to remove identity", e); |
| } |
| } |
| |
| /** |
| * Used to convert a dn of String to a Dn object |
| * @param principalName The principal name to be convert. |
| * @return |
| * @throws org.apache.directory.api.ldap.model.exception.LdapInvalidDnException if a remote exception occurs. |
| */ |
| private Dn toDn(String principalName) throws LdapInvalidDnException { |
| String[] names = principalName.split("@"); |
| String uid = names[0]; |
| Dn dn = new Dn(new Rdn("uid", uid), new Dn(getConfig().getString("base_dn"))); |
| return dn; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected Iterable<String> doGetIdentities() { |
| List<String> identityNames = new ArrayList<>(); |
| EntryCursor cursor; |
| Entry entry; |
| try { |
| cursor = connection.search(getConfig().getString("base_dn"), |
| "(objectclass=*)", SearchScope.ONELEVEL, KerberosAttribute.KRB5_PRINCIPAL_NAME_AT); |
| if (cursor == null) { |
| return null; |
| } |
| while (cursor.next()) { |
| entry = cursor.get(); |
| identityNames.add(entry.get(KerberosAttribute.KRB5_PRINCIPAL_NAME_AT).getString()); |
| } |
| cursor.close(); |
| Collections.sort(identityNames); |
| } catch (LdapException e) { |
| LOG.error("With LdapException when LdapConnection searching. " + e); |
| } catch (CursorException e) { |
| LOG.error("With CursorException when EntryCursor getting. " + e); |
| } catch (IOException e) { |
| LOG.error("With IOException when closing EntryCursor. " + e); |
| } |
| return identityNames; |
| } |
| } |