You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ze...@apache.org on 2018/01/15 02:38:45 UTC

directory-kerby git commit: DIRKRB-678 Implement MySQL identity backend for KDC server.

Repository: directory-kerby
Updated Branches:
  refs/heads/trunk 7b30a96ee -> 11089e869


DIRKRB-678 Implement MySQL identity backend for KDC server.


Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/commit/11089e86
Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/11089e86
Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/11089e86

Branch: refs/heads/trunk
Commit: 11089e86940723fb2ca5850504b93814b4e794ad
Parents: 7b30a96
Author: zenglinx <fr...@intel.com>
Authored: Mon Jan 15 10:38:30 2018 +0800
Committer: zenglinx <fr...@intel.com>
Committed: Mon Jan 15 10:38:30 2018 +0800

----------------------------------------------------------------------
 kerby-backend/mysql-backend/pom.xml             |  91 ++++
 .../kdc/identitybackend/MySQLConfKey.java       |  52 +++
 .../identitybackend/MySQLIdentityBackend.java   | 437 +++++++++++++++++++
 .../identity/backend/MySQLBackendKdcTest.java   |  78 ++++
 .../kerb/identity/backend/MySQLBackendTest.java |  57 +++
 kerby-backend/pom.xml                           |   1 +
 kerby-dist/kdc-dist/conf/backend.conf           |   4 +
 kerby-dist/kdc-dist/pom.xml                     |   7 +
 8 files changed, 727 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/mysql-backend/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-backend/mysql-backend/pom.xml b/kerby-backend/mysql-backend/pom.xml
new file mode 100644
index 0000000..d24335a
--- /dev/null
+++ b/kerby-backend/mysql-backend/pom.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.kerby</groupId>
+    <artifactId>kerby-backend</artifactId>
+    <version>1.1.1-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>mysql-backend</artifactId>
+  <name>MySQL identity backend</name>
+  <description>MySQL identity backend</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.directory.server</groupId>
+      <artifactId>apacheds-core-api</artifactId>
+      <version>2.0.0-M23</version>
+    </dependency>
+    <dependency>
+      <groupId>commons-dbutils</groupId>
+      <artifactId>commons-dbutils</artifactId>
+      <version>1.6</version>
+    </dependency>
+    <dependency>
+      <groupId>org.drizzle.jdbc</groupId>
+      <artifactId>drizzle-jdbc</artifactId>
+      <version>1.4</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerby-config</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-identity</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.11</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+       <groupId>com.h2database</groupId>
+       <artifactId>h2</artifactId>
+       <version>1.4.196</version>
+       <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-identity-test</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>kerb-kdc-test</artifactId>
+      <version>${project.version}</version>
+      <type>test-jar</type>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
----------------------------------------------------------------------
diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
new file mode 100644
index 0000000..ce2ead6
--- /dev/null
+++ b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
@@ -0,0 +1,52 @@
+/**
+ *  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.kerby.config.ConfigKey;
+
+/**
+ * Define all the MySQL backend related configuration items with default values.
+ */
+public enum MySQLConfKey implements ConfigKey {
+    MYSQL_DRIVER("org.drizzle.jdbc.DrizzleDriver"),
+    MYSQL_URL("jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend"),
+    MYSQL_USER("root"),
+    MYSQL_PASSWORD("passwd");
+
+    private Object defaultValue;
+
+    MySQLConfKey() {
+        this.defaultValue = null;
+    }
+
+    MySQLConfKey(Object defaultValue) {
+        this.defaultValue = defaultValue;
+    }
+
+    @Override
+    public String getPropertyKey() {
+        return name().toLowerCase();
+    }
+
+    @Override
+    public Object getDefaultValue() {
+        return this.defaultValue;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
----------------------------------------------------------------------
diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
new file mode 100644
index 0000000..32ede90
--- /dev/null
+++ b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
@@ -0,0 +1,437 @@
+/**
+ *  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.commons.dbutils.DbUtils;
+import org.apache.directory.api.util.GeneralizedTime;
+import org.apache.kerby.config.Config;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.identity.backend.AbstractIdentityBackend;
+import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
+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.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.sql.rowset.serial.SerialBlob;
+
+/**
+ * A MySQL based backend implementation.
+ */
+public class MySQLIdentityBackend extends AbstractIdentityBackend {
+    private Connection connection;
+    private String driver;
+    private String url;
+    private String user;
+    private String password;
+    private static final Logger LOG = LoggerFactory.getLogger(MySQLIdentityBackend.class);
+    private String identityTable;
+    private String keyInfoTable;
+
+    /**
+     * Constructing an instance using specified config that contains anything
+     * to be used to initialize an MySQL Backend.
+     * @param config . The config is used to config the backend.
+     */
+    public MySQLIdentityBackend(final Config config) {
+        setConfig(config);
+    }
+
+    public MySQLIdentityBackend() { }
+
+    /**
+     * Start the MySQL connection.
+     */
+    private void startConnection() throws KrbException {
+        try {
+            Class.forName(driver);
+            connection = DriverManager.getConnection(url, user, password);
+            if (!connection.isClosed()) {
+                LOG.info("Succeeded in connecting to MySQL.");
+            }
+        } catch (ClassNotFoundException e) {
+            throw new KrbException("JDBC Driver Class not found. ", e);
+        } catch (SQLException e) {
+            throw new KrbException("Failed to connecting to MySQL. ", e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doInitialize() throws KrbException {
+        LOG.info("Initializing the MySQL identity backend.");
+        driver = getConfig().getString(MySQLConfKey.MYSQL_DRIVER, true);
+        user = getConfig().getString(MySQLConfKey.MYSQL_USER, true);
+        password = getConfig().getString(MySQLConfKey.MYSQL_PASSWORD, true);
+
+        String urlString = getConfig().getString(MySQLConfKey.MYSQL_URL, true);
+        if (urlString == null || urlString.isEmpty()) {
+            urlString = getBackendConfig().getString(MySQLConfKey.MYSQL_URL, true);
+        }
+        url = urlString;
+
+        ResultSet resCheckTable = null;
+        PreparedStatement preInitialize = null;
+        PreparedStatement preKdcRealm = null;
+        ResultSet resKdcRealm = null;
+        PreparedStatement preIdentity = null;
+        PreparedStatement preKey = null;
+        try {
+            startConnection();
+
+            resCheckTable = connection.getMetaData().getTables(null, null, "kdc_config", null);
+            if (resCheckTable.next()) {
+                // Set initialized for kdc config table if HAS enabled
+                String stmInitialize = "UPDATE `kdc_config` SET initialized = true WHERE id = 1";
+                preInitialize = connection.prepareStatement(stmInitialize);
+                preInitialize.executeUpdate();
+
+                // Get identity table name according to realm of kdc
+                String stmKdcRealm = "SELECT realm FROM `kdc_config`";
+                preKdcRealm = connection.prepareStatement(stmKdcRealm);
+                resKdcRealm = preKdcRealm.executeQuery();
+                if (resKdcRealm.next()) {
+                    String realm = resKdcRealm.getString("realm").toLowerCase();
+                    identityTable = "`" + realm + "_identity" + "`";
+                    keyInfoTable = "`" + realm + "_key" + "`";
+                } else {
+                    throw new KrbException("Failed to get kdc config.");
+                }
+            } else {
+                identityTable = "`" + "kerby_identity" + "`";
+                keyInfoTable = "`" + "kerby_key" + "`";
+            }
+
+            // Create identity table
+            String stmIdentity = "CREATE TABLE IF NOT EXISTS " + identityTable
+                + " (principal varchar(255) NOT NULL, key_version INTEGER "
+                + "DEFAULT 1, kdc_flags INTEGER DEFAULT 0, disabled bool "
+                + "DEFAULT NULL, locked bool DEFAULT NULL, created_time "
+                + "VARCHAR(255) DEFAULT NULL, expire_time VARCHAR(255) "
+                + "DEFAULT NULL, PRIMARY KEY (principal) ) ENGINE=INNODB "
+                + "DEFAULT CHARSET=utf8;";
+            preIdentity = connection.prepareStatement(stmIdentity);
+            preIdentity.executeUpdate();
+
+            // Create key table
+            String stmKey = "CREATE TABLE IF NOT EXISTS " + keyInfoTable
+                + " (key_id INTEGER NOT NULL AUTO_INCREMENT, key_type "
+                + "VARCHAR(255) DEFAULT NULL, kvno INTEGER DEFAULT -1, "
+                + "key_value BLOB DEFAULT NULL, principal VARCHAR(255) NOT NULL,"
+                + "PRIMARY KEY (key_id), INDEX (principal), FOREIGN KEY "
+                + "(principal) REFERENCES " + identityTable + "(principal) "
+                + ") ENGINE=INNODB DEFAULT CHARSET=utf8;";
+            preKey = connection.prepareStatement(stmKey);
+            preKey.executeUpdate();
+
+        } catch (SQLException e) {
+            LOG.error("Error occurred while initialize MySQL backend." + e.toString());
+            throw new KrbException("Failed to create table in database. ", e);
+        } finally {
+            DbUtils.closeQuietly(resCheckTable);
+            DbUtils.closeQuietly(preInitialize);
+            DbUtils.closeQuietly(preKdcRealm);
+            DbUtils.closeQuietly(resKdcRealm);
+            DbUtils.closeQuietly(preIdentity);
+            DbUtils.closeQuietly(preKey);
+            doStop();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doStop() throws KrbException {
+        try {
+            closeConnection();
+            if (connection.isClosed()) {
+                LOG.info("Succeeded in closing connection with MySQL.");
+            }
+        } catch (SQLException e) {
+            LOG.error("Failed to close connection with MySQL.");
+            throw new KrbException("Failed to close connection with MySQL. ", e);
+        }
+    }
+
+    /**
+     * Close the connection for stop().
+     * @throws SQLException if SQLException handled
+     */
+    private void closeConnection() throws SQLException {
+        if (!connection.isClosed()) {
+            connection.close();
+        }
+    }
+
+    /**
+     * Convert a KerberosTime type object to a generalized time form of String.
+     * @param kerberosTime The kerberos time to convert
+     */
+    private String toGeneralizedTime(final KerberosTime kerberosTime) {
+        GeneralizedTime generalizedTime = new GeneralizedTime(kerberosTime.getValue());
+        return generalizedTime.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected KrbIdentity doAddIdentity(KrbIdentity identity) throws KrbException {
+        String principalName = identity.getPrincipalName();
+        int keyVersion = identity.getKeyVersion();
+        int kdcFlags = identity.getKdcFlags();
+        boolean disabled = identity.isDisabled();
+        boolean locked = identity.isLocked();
+        String createdTime = toGeneralizedTime(identity.getCreatedTime());
+        String expireTime = toGeneralizedTime(identity.getExpireTime());
+        Map<EncryptionType, EncryptionKey> keys = identity.getKeys();
+
+        PreparedStatement preIdentity = null;
+        PreparedStatement preKey = null;
+
+        KrbIdentity duplicateIdentity = doGetIdentity(principalName);
+        if (duplicateIdentity != null) {
+            LOG.warn("The identity maybe duplicate.");
+
+            return duplicateIdentity;
+        } else {
+            try {
+                startConnection();
+                connection.setAutoCommit(false);
+
+                // Insert identity to identity table
+                String stmIdentity = "insert into " + identityTable
+                        + " (principal, key_version, kdc_flags, disabled, locked, created_time, expire_time)"
+                        + " values(?, ?, ?, ?, ?, ?, ?)";
+                preIdentity = connection.prepareStatement(stmIdentity);
+                preIdentity.setString(1, principalName);
+                preIdentity.setInt(2, keyVersion);
+                preIdentity.setInt(3, kdcFlags);
+                preIdentity.setBoolean(4, disabled);
+                preIdentity.setBoolean(5, locked);
+                preIdentity.setString(6, createdTime);
+                preIdentity.setString(7, expireTime);
+                preIdentity.executeUpdate();
+
+                // Insert keys to key table
+                for (Map.Entry<EncryptionType, EncryptionKey> entry : keys.entrySet()) {
+                    String stmKey = "insert into " + keyInfoTable + " (key_type, kvno, key_value, principal)"
+                        + " values(?, ?, ?, ?)";
+                    preKey = connection.prepareStatement(stmKey);
+                    preKey.setString(1, entry.getKey().getName());
+                    preKey.setInt(2, entry.getValue().getKvno());
+                    preKey.setBlob(3, new SerialBlob(entry.getValue().getKeyData()));
+                    preKey.setString(4, principalName);
+                    preKey.executeUpdate();
+                }
+
+                connection.commit();
+                return identity;
+            } catch (SQLException e) {
+                try {
+                    LOG.info("Transaction is being rolled back.");
+                    connection.rollback();
+                } catch (SQLException ex) {
+                    throw new KrbException("Transaction roll back failed. ", ex);
+                }
+                LOG.error("Error occurred while adding identity.");
+                throw new KrbException("Failed to add identity. ", e);
+            } finally {
+                DbUtils.closeQuietly(preIdentity);
+                DbUtils.closeQuietly(preKey);
+                doStop();
+            }
+        }
+    }
+
+    /**
+     * Create kerberos time.
+     * @param generalizedTime generalized time
+     * @throws ParseException parse exception
+     */
+    private KerberosTime createKerberosTime(final String generalizedTime) throws ParseException {
+        long time = new GeneralizedTime(generalizedTime).getTime();
+        return new KerberosTime(time);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected KrbIdentity doGetIdentity(final String principalName) throws KrbException {
+        KrbIdentity krbIdentity = new KrbIdentity(principalName);
+
+        PreparedStatement preIdentity = null;
+        ResultSet resIdentity = null;
+        PreparedStatement preKey = null;
+        ResultSet resKey = null;
+        try {
+            startConnection();
+
+            // Get identity from identity table
+            String stmIdentity = "SELECT * FROM " + identityTable + " where principal = ?";
+            preIdentity = connection.prepareStatement(stmIdentity);
+            preIdentity.setString(1, principalName);
+            resIdentity = preIdentity.executeQuery();
+
+            if (!resIdentity.isBeforeFirst()) {
+                return null;
+            }
+
+            while (resIdentity.next()) {
+                krbIdentity.setKeyVersion(resIdentity.getInt("key_version"));
+                krbIdentity.setKdcFlags(resIdentity.getInt("kdc_flags"));
+                krbIdentity.setDisabled(resIdentity.getBoolean("disabled"));
+                krbIdentity.setLocked(resIdentity.getBoolean("locked"));
+                krbIdentity.setCreatedTime(createKerberosTime(resIdentity.getString("created_time")));
+                krbIdentity.setExpireTime(createKerberosTime(resIdentity.getString("expire_time")));
+            }
+
+            // Get keys from key table
+            List<EncryptionKey> keys = new ArrayList<>();
+            String stmKey = "SELECT * FROM " + keyInfoTable + " where principal = ?";
+            preKey = connection.prepareStatement(stmKey);
+            preKey.setString(1, principalName);
+            resKey = preKey.executeQuery();
+            while (resKey.next()) {
+                int kvno = resKey.getInt("kvno");
+                String keyType = resKey.getString("key_type");
+                EncryptionType eType = EncryptionType.fromName(keyType);
+                byte[] keyValue = resKey.getBytes("key_value");
+                EncryptionKey key = new EncryptionKey(eType, keyValue, kvno);
+                keys.add(key);
+            }
+
+            krbIdentity.addKeys(keys);
+            return krbIdentity;
+        } catch (SQLException e) {
+            LOG.error("Error occurred while getting identity.");
+            throw new KrbException("Failed to get identity. ", e);
+        } catch (ParseException e) {
+            throw new KrbException("Failed to get identity. ", e);
+        } finally {
+            DbUtils.closeQuietly(preIdentity);
+            DbUtils.closeQuietly(resIdentity);
+            DbUtils.closeQuietly(preKey);
+            DbUtils.closeQuietly(resKey);
+            doStop();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected KrbIdentity doUpdateIdentity(KrbIdentity identity) throws KrbException {
+        String principalName = identity.getPrincipalName();
+        try {
+            doDeleteIdentity(principalName); // Delete former identity
+            doAddIdentity(identity); // Insert new identity
+        } catch (KrbException e) {
+            LOG.error("Error occurred while updating identity: " + principalName);
+            throw new KrbException("Failed to update identity. ", e);
+        }
+
+        return getIdentity(principalName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void doDeleteIdentity(String principalName) throws KrbException {
+        PreparedStatement preKey = null;
+        PreparedStatement preIdentity = null;
+        try {
+            startConnection();
+            connection.setAutoCommit(false);
+
+            // Delete keys from key table
+            String stmKey = "DELETE FROM  " + keyInfoTable + " where principal = ?";
+            preKey = connection.prepareStatement(stmKey);
+            preKey.setString(1, principalName);
+            preKey.executeUpdate();
+
+            // Dlete identity from identity table
+            String stmIdentity = "DELETE FROM " + identityTable + " where principal = ? ";
+            preIdentity = connection.prepareStatement(stmIdentity);
+            preIdentity.setString(1, principalName);
+            preIdentity.executeUpdate();
+
+            connection.commit();
+        } catch (SQLException e) {
+            try {
+                LOG.info("Transaction is being rolled back.");
+                connection.rollback();
+            } catch (SQLException ex) {
+                throw new KrbException("Transaction roll back failed. ", ex);
+            }
+            LOG.error("Error occurred while deleting identity.");
+            throw new KrbException("Failed to delete identity. ", e);
+        } finally {
+            DbUtils.closeQuietly(preIdentity);
+            DbUtils.closeQuietly(preKey);
+            doStop();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Iterable<String> doGetIdentities() throws KrbException {
+        List<String> identityNames = new ArrayList<>();
+        PreparedStatement preSmt = null;
+        ResultSet result = null;
+        try {
+            startConnection();
+            String statement = "SELECT * FROM " + identityTable;
+            preSmt = connection.prepareStatement(statement);
+            result = preSmt.executeQuery();
+            while (result.next()) {
+                identityNames.add(result.getString("principal"));
+            }
+            result.close();
+            preSmt.close();
+        } catch (SQLException e) {
+            LOG.error("Error occurred while getting identities.");
+            throw new KrbException("Failed to get identities. ", e);
+        } finally {
+            DbUtils.closeQuietly(preSmt);
+            DbUtils.closeQuietly(result);
+            doStop();
+        }
+
+        return identityNames;
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
----------------------------------------------------------------------
diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java b/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
new file mode 100644
index 0000000..565a0ee
--- /dev/null
+++ b/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
@@ -0,0 +1,78 @@
+/**
+ *  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.kerb.identity.backend;
+
+import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
+import org.apache.kerby.kerberos.kerb.server.KdcTestBase;
+import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
+import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class MySQLBackendKdcTest extends KdcTestBase {
+    private static File testDir = new File(System.getProperty("test.dir", "target"));
+    private static File dbFile = new File(testDir, "mysqlbackend.mv.db");
+
+    @After
+    public void tearDown() {
+        if (dbFile.exists() && !dbFile.delete()) {
+            System.err.println("Failed to delete the test database file.");
+        }
+    }
+
+    @Override
+    protected void prepareKdc() throws KrbException {
+        BackendConfig backendConfig = getKdcServer().getBackendConfig();
+
+        backendConfig.setString(KdcConfigKey.KDC_IDENTITY_BACKEND,
+                "org.apache.kerby.kerberos.kdc.identitybackend.MySQLIdentityBackend");
+        backendConfig.setString(MySQLConfKey.MYSQL_DRIVER, "org.h2.Driver");
+        backendConfig.setString(MySQLConfKey.MYSQL_URL,
+                "jdbc:h2:" + testDir.getAbsolutePath() + "/mysqlbackend;MODE=MySQL");
+        backendConfig.setString(MySQLConfKey.MYSQL_USER, "root");
+        backendConfig.setString(MySQLConfKey.MYSQL_PASSWORD, "123456");
+
+        super.prepareKdc();
+    }
+
+    @Test
+    public void testKdc() {
+        TgtTicket tgt;
+        SgtTicket tkt;
+
+        try {
+            tgt = getKrbClient().requestTgt(getClientPrincipal(), getClientPassword());
+            assertThat(tgt).isNotNull();
+
+            tkt = getKrbClient().requestSgt(tgt, getServerPrincipal());
+            assertThat(tkt).isNotNull();
+        } catch (Exception e) {
+            Assert.fail("Exception occurred with good password. "
+                    + e.toString());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
----------------------------------------------------------------------
diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java b/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
new file mode 100644
index 0000000..59f3500
--- /dev/null
+++ b/kerby-backend/mysql-backend/src/test/java/org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
@@ -0,0 +1,57 @@
+/**
+ *  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.kerb.identity.backend;
+
+import org.apache.kerby.config.Conf;
+import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
+import org.apache.kerby.kerberos.kdc.identitybackend.MySQLIdentityBackend;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.io.IOException;
+
+public class MySQLBackendTest extends BackendTestBase {
+    private static File testDir = new File(System.getProperty("test.dir", "target"));
+    private static File dbFile = new File(testDir, "mysqlbackend.mv.db");
+
+    @BeforeClass
+    public static void setup() throws KrbException, IOException {
+        Conf config = new Conf();
+        config.setString(MySQLConfKey.MYSQL_DRIVER, "org.h2.Driver");
+        config.setString(MySQLConfKey.MYSQL_URL,
+                "jdbc:h2:" + testDir.getCanonicalPath() + "/mysqlbackend;MODE=MySQL");
+        config.setString(MySQLConfKey.MYSQL_USER, "root");
+        config.setString(MySQLConfKey.MYSQL_PASSWORD, "123456");
+        backend = new MySQLIdentityBackend(config);
+        backend.initialize();
+    }
+
+    @AfterClass
+    public static void tearDown() throws KrbException {
+        if (backend != null) {
+            backend.stop();
+        }
+        if (dbFile.exists() && !dbFile.delete()) {
+            System.err.println("Failed to delete the test database file.");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-backend/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-backend/pom.xml b/kerby-backend/pom.xml
index ef95b87..6ce432c 100644
--- a/kerby-backend/pom.xml
+++ b/kerby-backend/pom.xml
@@ -53,6 +53,7 @@
         <module>ldap-backend</module>
         <module>mavibot-backend</module>
         <module>zookeeper-backend</module>
+        <module>mysql-backend</module>
       </modules>
     </profile>
   </profiles>

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-dist/kdc-dist/conf/backend.conf
----------------------------------------------------------------------
diff --git a/kerby-dist/kdc-dist/conf/backend.conf b/kerby-dist/kdc-dist/conf/backend.conf
index 2ead268..20134ef 100644
--- a/kerby-dist/kdc-dist/conf/backend.conf
+++ b/kerby-dist/kdc-dist/conf/backend.conf
@@ -22,3 +22,7 @@ embedded_zk = false
 zk_host = 127.0.0.1
 zk_port = 2181
 data_dir = /tmp/zookeeper/data
+mysql_driver = org.drizzle.jdbc.DrizzleDriver
+mysql_url = jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend?createDB=true
+mysql_user = root
+mysql_password = passwd

http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/11089e86/kerby-dist/kdc-dist/pom.xml
----------------------------------------------------------------------
diff --git a/kerby-dist/kdc-dist/pom.xml b/kerby-dist/kdc-dist/pom.xml
index 1766d28..3a5de2d 100644
--- a/kerby-dist/kdc-dist/pom.xml
+++ b/kerby-dist/kdc-dist/pom.xml
@@ -89,6 +89,13 @@
       <version>${gson.version}</version>
     </dependency>
 
+    <!-- For mysql backend -->
+    <dependency>
+      <groupId>org.apache.kerby</groupId>
+      <artifactId>mysql-backend</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
     <!-- For common and misc -->
     <dependency>
       <groupId>org.slf4j</groupId>


RE: directory-kerby git commit: DIRKRB-678 Implement MySQL identity backend for KDC server.

Posted by "Zeng, Frank" <fr...@intel.com>.
Hi Colm,

Thanks for your suggestion.

a) I will fix it soon.
b) I will use the KerberosTime.getTime() method to save the Kerberos time column of identity.
c) yes, thanks for your remind.

P.S. I created a new issue named DIRKRB-686 for these refactor work.

Thanks,
Frank


-----Original Message-----
From: Colm O hEigeartaigh [mailto:coheigea@apache.org] 
Sent: Monday, January 15, 2018 5:49 PM
To: kerby@directory.apache.org
Subject: Re: directory-kerby git commit: DIRKRB-678 Implement MySQL identity backend for KDC server.

Hi Frank,

I'd like to suggest a few small changes to this commit:

a) The mysql-backend uses different versions for junit + Apache Directory as defined in the parent pom. So instead of 2.0.0-M23 for Apache Directory you can use ${apacheds.version}. You can remove the junit version + test scope because it's defined in the root pom under dependencyManagement.

b) The Apache Directory API is only used to call on GeneralizedTime, but this pulls in a large number of dependencies in the distribution. Can we replace GeneralizedTime with something else, or just pull GeneralizedTime into our project directly?

c) I think the mysql-backend dependency should be commented out in the kerby-dist pom.

Colm.

On Mon, Jan 15, 2018 at 2:38 AM, <ze...@apache.org> wrote:

> Repository: directory-kerby
> Updated Branches:
>   refs/heads/trunk 7b30a96ee -> 11089e869
>
>
> DIRKRB-678 Implement MySQL identity backend for KDC server.
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/11089e86
> Tree: 
> http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/11089e86
> Diff: 
> http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/11089e86
>
> Branch: refs/heads/trunk
> Commit: 11089e86940723fb2ca5850504b93814b4e794ad
> Parents: 7b30a96
> Author: zenglinx <fr...@intel.com>
> Authored: Mon Jan 15 10:38:30 2018 +0800
> Committer: zenglinx <fr...@intel.com>
> Committed: Mon Jan 15 10:38:30 2018 +0800
>
> ----------------------------------------------------------------------
>  kerby-backend/mysql-backend/pom.xml             |  91 ++++
>  .../kdc/identitybackend/MySQLConfKey.java       |  52 +++
>  .../identitybackend/MySQLIdentityBackend.java   | 437 +++++++++++++++++++
>  .../identity/backend/MySQLBackendKdcTest.java   |  78 ++++
>  .../kerb/identity/backend/MySQLBackendTest.java |  57 +++
>  kerby-backend/pom.xml                           |   1 +
>  kerby-dist/kdc-dist/conf/backend.conf           |   4 +
>  kerby-dist/kdc-dist/pom.xml                     |   7 +
>  8 files changed, 727 insertions(+)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/pom.xml
> b/kerby-backend/mysql-backend/pom.xml
> new file mode 100644
> index 0000000..d24335a
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/pom.xml
> @@ -0,0 +1,91 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<!--
> +  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. See accompanying LICENSE file.
> +-->
> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://
> maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
> +  <modelVersion>4.0.0</modelVersion>
> +
> +  <parent>
> +    <groupId>org.apache.kerby</groupId>
> +    <artifactId>kerby-backend</artifactId>
> +    <version>1.1.1-SNAPSHOT</version>  </parent>
> +
> +  <artifactId>mysql-backend</artifactId>
> +  <name>MySQL identity backend</name>  <description>MySQL identity 
> + backend</description>
> +
> +  <dependencies>
> +    <dependency>
> +      <groupId>org.apache.directory.server</groupId>
> +      <artifactId>apacheds-core-api</artifactId>
> +      <version>2.0.0-M23</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>commons-dbutils</groupId>
> +      <artifactId>commons-dbutils</artifactId>
> +      <version>1.6</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.drizzle.jdbc</groupId>
> +      <artifactId>drizzle-jdbc</artifactId>
> +      <version>1.4</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerby-config</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-core</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-identity</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-common</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>junit</groupId>
> +      <artifactId>junit</artifactId>
> +      <version>4.11</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +       <groupId>com.h2database</groupId>
> +       <artifactId>h2</artifactId>
> +       <version>1.4.196</version>
> +       <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-identity-test</artifactId>
> +      <version>${project.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-kdc-test</artifactId>
> +      <version>${project.version}</version>
> +      <type>test-jar</type>
> +      <scope>test</scope>
> +    </dependency>
> +  </dependencies>
> +
> +</project>
> \ No newline at end of file
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/main/java/
> org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/
> kdc/
> identitybackend/MySQLConfKey.java
> new file mode 100644
> index 0000000..ce2ead6
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> @@ -0,0 +1,52 @@
> +/**
> + *  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.kerby.config.ConfigKey;
> +
> +/**
> + * Define all the MySQL backend related configuration items with 
> +default
> values.
> + */
> +public enum MySQLConfKey implements ConfigKey {
> +    MYSQL_DRIVER("org.drizzle.jdbc.DrizzleDriver"),
> +    MYSQL_URL("jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend"),
> +    MYSQL_USER("root"),
> +    MYSQL_PASSWORD("passwd");
> +
> +    private Object defaultValue;
> +
> +    MySQLConfKey() {
> +        this.defaultValue = null;
> +    }
> +
> +    MySQLConfKey(Object defaultValue) {
> +        this.defaultValue = defaultValue;
> +    }
> +
> +    @Override
> +    public String getPropertyKey() {
> +        return name().toLowerCase();
> +    }
> +
> +    @Override
> +    public Object getDefaultValue() {
> +        return this.defaultValue;
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/main/java/
> org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.jav
> a
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
> b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/
> kdc/ identitybackend/MySQLIdentityBackend.java
> new file mode 100644
> index 0000000..32ede90
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
> @@ -0,0 +1,437 @@
> +/**
> + *  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.commons.dbutils.DbUtils;
> +import org.apache.directory.api.util.GeneralizedTime;
> +import org.apache.kerby.config.Config; import 
> +org.apache.kerby.kerberos.kerb.KrbException;
> +import org.apache.kerby.kerberos.kerb.identity.backend.
> AbstractIdentityBackend;
> +import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
> +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.sql.Connection;
> +import java.sql.DriverManager;
> +import java.sql.PreparedStatement;
> +import java.sql.ResultSet;
> +import java.sql.SQLException;
> +import java.text.ParseException;
> +import java.util.ArrayList;
> +import java.util.List;
> +import java.util.Map;
> +import javax.sql.rowset.serial.SerialBlob;
> +
> +/**
> + * A MySQL based backend implementation.
> + */
> +public class MySQLIdentityBackend extends AbstractIdentityBackend {
> +    private Connection connection;
> +    private String driver;
> +    private String url;
> +    private String user;
> +    private String password;
> +    private static final Logger LOG = LoggerFactory.getLogger(
> MySQLIdentityBackend.class);
> +    private String identityTable;
> +    private String keyInfoTable;
> +
> +    /**
> +     * Constructing an instance using specified config that contains
> anything
> +     * to be used to initialize an MySQL Backend.
> +     * @param config . The config is used to config the backend.
> +     */
> +    public MySQLIdentityBackend(final Config config) {
> +        setConfig(config);
> +    }
> +
> +    public MySQLIdentityBackend() { }
> +
> +    /**
> +     * Start the MySQL connection.
> +     */
> +    private void startConnection() throws KrbException {
> +        try {
> +            Class.forName(driver);
> +            connection = DriverManager.getConnection(url, user,
> password);
> +            if (!connection.isClosed()) {
> +                LOG.info("Succeeded in connecting to MySQL.");
> +            }
> +        } catch (ClassNotFoundException e) {
> +            throw new KrbException("JDBC Driver Class not found. ", e);
> +        } catch (SQLException e) {
> +            throw new KrbException("Failed to connecting to MySQL. ", e);
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doInitialize() throws KrbException {
> +        LOG.info("Initializing the MySQL identity backend.");
> +        driver = getConfig().getString(MySQLConfKey.MYSQL_DRIVER, true);
> +        user = getConfig().getString(MySQLConfKey.MYSQL_USER, true);
> +        password = getConfig().getString(MySQLConfKey.MYSQL_PASSWORD,
> true);
> +
> +        String urlString = 
> + getConfig().getString(MySQLConfKey.MYSQL_URL,
> true);
> +        if (urlString == null || urlString.isEmpty()) {
> +            urlString = 
> + getBackendConfig().getString(MySQLConfKey.MYSQL_URL,
> true);
> +        }
> +        url = urlString;
> +
> +        ResultSet resCheckTable = null;
> +        PreparedStatement preInitialize = null;
> +        PreparedStatement preKdcRealm = null;
> +        ResultSet resKdcRealm = null;
> +        PreparedStatement preIdentity = null;
> +        PreparedStatement preKey = null;
> +        try {
> +            startConnection();
> +
> +            resCheckTable = connection.getMetaData().getTables(null,
> null, "kdc_config", null);
> +            if (resCheckTable.next()) {
> +                // Set initialized for kdc config table if HAS enabled
> +                String stmInitialize = "UPDATE `kdc_config` SET
> initialized = true WHERE id = 1";
> +                preInitialize = connection.prepareStatement(
> stmInitialize);
> +                preInitialize.executeUpdate();
> +
> +                // Get identity table name according to realm of kdc
> +                String stmKdcRealm = "SELECT realm FROM `kdc_config`";
> +                preKdcRealm = connection.prepareStatement(stmKdcRealm);
> +                resKdcRealm = preKdcRealm.executeQuery();
> +                if (resKdcRealm.next()) {
> +                    String realm = resKdcRealm.getString("realm")
> .toLowerCase();
> +                    identityTable = "`" + realm + "_identity" + "`";
> +                    keyInfoTable = "`" + realm + "_key" + "`";
> +                } else {
> +                    throw new KrbException("Failed to get kdc config.");
> +                }
> +            } else {
> +                identityTable = "`" + "kerby_identity" + "`";
> +                keyInfoTable = "`" + "kerby_key" + "`";
> +            }
> +
> +            // Create identity table
> +            String stmIdentity = "CREATE TABLE IF NOT EXISTS " +
> identityTable
> +                + " (principal varchar(255) NOT NULL, key_version 
> + INTEGER
> "
> +                + "DEFAULT 1, kdc_flags INTEGER DEFAULT 0, disabled bool "
> +                + "DEFAULT NULL, locked bool DEFAULT NULL, created_time "
> +                + "VARCHAR(255) DEFAULT NULL, expire_time VARCHAR(255) "
> +                + "DEFAULT NULL, PRIMARY KEY (principal) ) ENGINE=INNODB "
> +                + "DEFAULT CHARSET=utf8;";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.executeUpdate();
> +
> +            // Create key table
> +            String stmKey = "CREATE TABLE IF NOT EXISTS " + keyInfoTable
> +                + " (key_id INTEGER NOT NULL AUTO_INCREMENT, key_type "
> +                + "VARCHAR(255) DEFAULT NULL, kvno INTEGER DEFAULT -1, "
> +                + "key_value BLOB DEFAULT NULL, principal 
> + VARCHAR(255)
> NOT NULL,"
> +                + "PRIMARY KEY (key_id), INDEX (principal), FOREIGN KEY "
> +                + "(principal) REFERENCES " + identityTable +
> "(principal) "
> +                + ") ENGINE=INNODB DEFAULT CHARSET=utf8;";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.executeUpdate();
> +
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while initialize MySQL 
> + backend." +
> e.toString());
> +            throw new KrbException("Failed to create table in database.
> ", e);
> +        } finally {
> +            DbUtils.closeQuietly(resCheckTable);
> +            DbUtils.closeQuietly(preInitialize);
> +            DbUtils.closeQuietly(preKdcRealm);
> +            DbUtils.closeQuietly(resKdcRealm);
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doStop() throws KrbException {
> +        try {
> +            closeConnection();
> +            if (connection.isClosed()) {
> +                LOG.info("Succeeded in closing connection with MySQL.");
> +            }
> +        } catch (SQLException e) {
> +            LOG.error("Failed to close connection with MySQL.");
> +            throw new KrbException("Failed to close connection with
> MySQL. ", e);
> +        }
> +    }
> +
> +    /**
> +     * Close the connection for stop().
> +     * @throws SQLException if SQLException handled
> +     */
> +    private void closeConnection() throws SQLException {
> +        if (!connection.isClosed()) {
> +            connection.close();
> +        }
> +    }
> +
> +    /**
> +     * Convert a KerberosTime type object to a generalized time form 
> + of
> String.
> +     * @param kerberosTime The kerberos time to convert
> +     */
> +    private String toGeneralizedTime(final KerberosTime kerberosTime) {
> +        GeneralizedTime generalizedTime = new
> GeneralizedTime(kerberosTime.getValue());
> +        return generalizedTime.toString();
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doAddIdentity(KrbIdentity identity) throws
> KrbException {
> +        String principalName = identity.getPrincipalName();
> +        int keyVersion = identity.getKeyVersion();
> +        int kdcFlags = identity.getKdcFlags();
> +        boolean disabled = identity.isDisabled();
> +        boolean locked = identity.isLocked();
> +        String createdTime = toGeneralizedTime(identity.
> getCreatedTime());
> +        String expireTime = toGeneralizedTime(identity.getExpireTime());
> +        Map<EncryptionType, EncryptionKey> keys = identity.getKeys();
> +
> +        PreparedStatement preIdentity = null;
> +        PreparedStatement preKey = null;
> +
> +        KrbIdentity duplicateIdentity = doGetIdentity(principalName);
> +        if (duplicateIdentity != null) {
> +            LOG.warn("The identity maybe duplicate.");
> +
> +            return duplicateIdentity;
> +        } else {
> +            try {
> +                startConnection();
> +                connection.setAutoCommit(false);
> +
> +                // Insert identity to identity table
> +                String stmIdentity = "insert into " + identityTable
> +                        + " (principal, key_version, kdc_flags, 
> + disabled,
> locked, created_time, expire_time)"
> +                        + " values(?, ?, ?, ?, ?, ?, ?)";
> +                preIdentity = connection.prepareStatement(stmIdentity);
> +                preIdentity.setString(1, principalName);
> +                preIdentity.setInt(2, keyVersion);
> +                preIdentity.setInt(3, kdcFlags);
> +                preIdentity.setBoolean(4, disabled);
> +                preIdentity.setBoolean(5, locked);
> +                preIdentity.setString(6, createdTime);
> +                preIdentity.setString(7, expireTime);
> +                preIdentity.executeUpdate();
> +
> +                // Insert keys to key table
> +                for (Map.Entry<EncryptionType, EncryptionKey> entry :
> keys.entrySet()) {
> +                    String stmKey = "insert into " + keyInfoTable + "
> (key_type, kvno, key_value, principal)"
> +                        + " values(?, ?, ?, ?)";
> +                    preKey = connection.prepareStatement(stmKey);
> +                    preKey.setString(1, entry.getKey().getName());
> +                    preKey.setInt(2, entry.getValue().getKvno());
> +                    preKey.setBlob(3, new SerialBlob(entry.getValue().
> getKeyData()));
> +                    preKey.setString(4, principalName);
> +                    preKey.executeUpdate();
> +                }
> +
> +                connection.commit();
> +                return identity;
> +            } catch (SQLException e) {
> +                try {
> +                    LOG.info("Transaction is being rolled back.");
> +                    connection.rollback();
> +                } catch (SQLException ex) {
> +                    throw new KrbException("Transaction roll back failed.
> ", ex);
> +                }
> +                LOG.error("Error occurred while adding identity.");
> +                throw new KrbException("Failed to add identity. ", e);
> +            } finally {
> +                DbUtils.closeQuietly(preIdentity);
> +                DbUtils.closeQuietly(preKey);
> +                doStop();
> +            }
> +        }
> +    }
> +
> +    /**
> +     * Create kerberos time.
> +     * @param generalizedTime generalized time
> +     * @throws ParseException parse exception
> +     */
> +    private KerberosTime createKerberosTime(final String 
> + generalizedTime)
> throws ParseException {
> +        long time = new GeneralizedTime(generalizedTime).getTime();
> +        return new KerberosTime(time);
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doGetIdentity(final String principalName)
> throws KrbException {
> +        KrbIdentity krbIdentity = new KrbIdentity(principalName);
> +
> +        PreparedStatement preIdentity = null;
> +        ResultSet resIdentity = null;
> +        PreparedStatement preKey = null;
> +        ResultSet resKey = null;
> +        try {
> +            startConnection();
> +
> +            // Get identity from identity table
> +            String stmIdentity = "SELECT * FROM " + identityTable + "
> where principal = ?";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.setString(1, principalName);
> +            resIdentity = preIdentity.executeQuery();
> +
> +            if (!resIdentity.isBeforeFirst()) {
> +                return null;
> +            }
> +
> +            while (resIdentity.next()) {
> +                krbIdentity.setKeyVersion(resIdentity.getInt("key_
> version"));
> +                krbIdentity.setKdcFlags(resIdentity.getInt("kdc_flags"));
> +                krbIdentity.setDisabled(resIdentity.getBoolean("
> disabled"));
> +                krbIdentity.setLocked(resIdentity.getBoolean("locked"));
> +                krbIdentity.setCreatedTime(createKerberosTime(
> resIdentity.getString("created_time")));
> +                krbIdentity.setExpireTime(createKerberosTime(
> resIdentity.getString("expire_time")));
> +            }
> +
> +            // Get keys from key table
> +            List<EncryptionKey> keys = new ArrayList<>();
> +            String stmKey = "SELECT * FROM " + keyInfoTable + " where
> principal = ?";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.setString(1, principalName);
> +            resKey = preKey.executeQuery();
> +            while (resKey.next()) {
> +                int kvno = resKey.getInt("kvno");
> +                String keyType = resKey.getString("key_type");
> +                EncryptionType eType = EncryptionType.fromName(keyType);
> +                byte[] keyValue = resKey.getBytes("key_value");
> +                EncryptionKey key = new EncryptionKey(eType, 
> + keyValue,
> kvno);
> +                keys.add(key);
> +            }
> +
> +            krbIdentity.addKeys(keys);
> +            return krbIdentity;
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while getting identity.");
> +            throw new KrbException("Failed to get identity. ", e);
> +        } catch (ParseException e) {
> +            throw new KrbException("Failed to get identity. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(resIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            DbUtils.closeQuietly(resKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doUpdateIdentity(KrbIdentity identity) 
> + throws
> KrbException {
> +        String principalName = identity.getPrincipalName();
> +        try {
> +            doDeleteIdentity(principalName); // Delete former identity
> +            doAddIdentity(identity); // Insert new identity
> +        } catch (KrbException e) {
> +            LOG.error("Error occurred while updating identity: " +
> principalName);
> +            throw new KrbException("Failed to update identity. ", e);
> +        }
> +
> +        return getIdentity(principalName);
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doDeleteIdentity(String principalName) throws
> KrbException {
> +        PreparedStatement preKey = null;
> +        PreparedStatement preIdentity = null;
> +        try {
> +            startConnection();
> +            connection.setAutoCommit(false);
> +
> +            // Delete keys from key table
> +            String stmKey = "DELETE FROM  " + keyInfoTable + " where
> principal = ?";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.setString(1, principalName);
> +            preKey.executeUpdate();
> +
> +            // Dlete identity from identity table
> +            String stmIdentity = "DELETE FROM " + identityTable + " 
> + where
> principal = ? ";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.setString(1, principalName);
> +            preIdentity.executeUpdate();
> +
> +            connection.commit();
> +        } catch (SQLException e) {
> +            try {
> +                LOG.info("Transaction is being rolled back.");
> +                connection.rollback();
> +            } catch (SQLException ex) {
> +                throw new KrbException("Transaction roll back failed. 
> + ",
> ex);
> +            }
> +            LOG.error("Error occurred while deleting identity.");
> +            throw new KrbException("Failed to delete identity. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected Iterable<String> doGetIdentities() throws KrbException {
> +        List<String> identityNames = new ArrayList<>();
> +        PreparedStatement preSmt = null;
> +        ResultSet result = null;
> +        try {
> +            startConnection();
> +            String statement = "SELECT * FROM " + identityTable;
> +            preSmt = connection.prepareStatement(statement);
> +            result = preSmt.executeQuery();
> +            while (result.next()) {
> +                identityNames.add(result.getString("principal"));
> +            }
> +            result.close();
> +            preSmt.close();
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while getting identities.");
> +            throw new KrbException("Failed to get identities. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preSmt);
> +            DbUtils.closeQuietly(result);
> +            doStop();
> +        }
> +
> +        return identityNames;
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/test/java/
> org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.ja
> va
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> new file mode 100644
> index 0000000..565a0ee
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> @@ -0,0 +1,78 @@
> +/**
> + *  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.kerb.identity.backend;
> +
> +import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
> +import org.apache.kerby.kerberos.kerb.KrbException;
> +import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
> +import org.apache.kerby.kerberos.kerb.server.KdcTestBase;
> +import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
> +import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
> +import org.junit.After;
> +import org.junit.Assert;
> +import org.junit.Test;
> +
> +import java.io.File;
> +
> +import static org.assertj.core.api.Assertions.assertThat;
> +
> +public class MySQLBackendKdcTest extends KdcTestBase {
> +    private static File testDir = new 
> +File(System.getProperty("test.dir",
> "target"));
> +    private static File dbFile = new File(testDir, 
> + "mysqlbackend.mv.db");
> +
> +    @After
> +    public void tearDown() {
> +        if (dbFile.exists() && !dbFile.delete()) {
> +            System.err.println("Failed to delete the test database
> file.");
> +        }
> +    }
> +
> +    @Override
> +    protected void prepareKdc() throws KrbException {
> +        BackendConfig backendConfig = 
> + getKdcServer().getBackendConfig();
> +
> +        backendConfig.setString(KdcConfigKey.KDC_IDENTITY_BACKEND,
> +                "org.apache.kerby.kerberos.kdc.identitybackend.
> MySQLIdentityBackend");
> +        backendConfig.setString(MySQLConfKey.MYSQL_DRIVER,
> "org.h2.Driver");
> +        backendConfig.setString(MySQLConfKey.MYSQL_URL,
> +                "jdbc:h2:" + testDir.getAbsolutePath() +
> "/mysqlbackend;MODE=MySQL");
> +        backendConfig.setString(MySQLConfKey.MYSQL_USER, "root");
> +        backendConfig.setString(MySQLConfKey.MYSQL_PASSWORD, 
> + "123456");
> +
> +        super.prepareKdc();
> +    }
> +
> +    @Test
> +    public void testKdc() {
> +        TgtTicket tgt;
> +        SgtTicket tkt;
> +
> +        try {
> +            tgt = getKrbClient().requestTgt(getClientPrincipal(),
> getClientPassword());
> +            assertThat(tgt).isNotNull();
> +
> +            tkt = getKrbClient().requestSgt(tgt, getServerPrincipal());
> +            assertThat(tkt).isNotNull();
> +        } catch (Exception e) {
> +            Assert.fail("Exception occurred with good password. "
> +                    + e.toString());
> +        }
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/test/java/
> org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> new file mode 100644
> index 0000000..59f3500
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> @@ -0,0 +1,57 @@
> +/**
> + *  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.kerb.identity.backend;
> +
> +import org.apache.kerby.config.Conf;
> +import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
> +import org.apache.kerby.kerberos.kdc.identitybackend.
> MySQLIdentityBackend;
> +import org.apache.kerby.kerberos.kerb.KrbException;
> +import org.junit.AfterClass;
> +import org.junit.BeforeClass;
> +
> +import java.io.File;
> +import java.io.IOException;
> +
> +public class MySQLBackendTest extends BackendTestBase {
> +    private static File testDir = new 
> +File(System.getProperty("test.dir",
> "target"));
> +    private static File dbFile = new File(testDir, 
> + "mysqlbackend.mv.db");
> +
> +    @BeforeClass
> +    public static void setup() throws KrbException, IOException {
> +        Conf config = new Conf();
> +        config.setString(MySQLConfKey.MYSQL_DRIVER, "org.h2.Driver");
> +        config.setString(MySQLConfKey.MYSQL_URL,
> +                "jdbc:h2:" + testDir.getCanonicalPath() +
> "/mysqlbackend;MODE=MySQL");
> +        config.setString(MySQLConfKey.MYSQL_USER, "root");
> +        config.setString(MySQLConfKey.MYSQL_PASSWORD, "123456");
> +        backend = new MySQLIdentityBackend(config);
> +        backend.initialize();
> +    }
> +
> +    @AfterClass
> +    public static void tearDown() throws KrbException {
> +        if (backend != null) {
> +            backend.stop();
> +        }
> +        if (dbFile.exists() && !dbFile.delete()) {
> +            System.err.println("Failed to delete the test database
> file.");
> +        }
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/pom.xml b/kerby-backend/pom.xml index 
> ef95b87..6ce432c 100644
> --- a/kerby-backend/pom.xml
> +++ b/kerby-backend/pom.xml
> @@ -53,6 +53,7 @@
>          <module>ldap-backend</module>
>          <module>mavibot-backend</module>
>          <module>zookeeper-backend</module>
> +        <module>mysql-backend</module>
>        </modules>
>      </profile>
>    </profiles>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-dist/kdc-dist/conf/backend.conf
> ----------------------------------------------------------------------
> diff --git a/kerby-dist/kdc-dist/conf/backend.conf
> b/kerby-dist/kdc-dist/conf/backend.conf
> index 2ead268..20134ef 100644
> --- a/kerby-dist/kdc-dist/conf/backend.conf
> +++ b/kerby-dist/kdc-dist/conf/backend.conf
> @@ -22,3 +22,7 @@ embedded_zk = false
>  zk_host = 127.0.0.1
>  zk_port = 2181
>  data_dir = /tmp/zookeeper/data
> +mysql_driver = org.drizzle.jdbc.DrizzleDriver mysql_url = 
> +jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend?createDB=true
> +mysql_user = root
> +mysql_password = passwd
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-dist/kdc-dist/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-dist/kdc-dist/pom.xml b/kerby-dist/kdc-dist/pom.xml 
> index 1766d28..3a5de2d 100644
> --- a/kerby-dist/kdc-dist/pom.xml
> +++ b/kerby-dist/kdc-dist/pom.xml
> @@ -89,6 +89,13 @@
>        <version>${gson.version}</version>
>      </dependency>
>
> +    <!-- For mysql backend -->
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>mysql-backend</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +
>      <!-- For common and misc -->
>      <dependency>
>        <groupId>org.slf4j</groupId>
>
>


--
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com

Re: directory-kerby git commit: DIRKRB-678 Implement MySQL identity backend for KDC server.

Posted by Colm O hEigeartaigh <co...@apache.org>.
Hi Frank,

I'd like to suggest a few small changes to this commit:

a) The mysql-backend uses different versions for junit + Apache Directory
as defined in the parent pom. So instead of 2.0.0-M23 for Apache Directory
you can use ${apacheds.version}. You can remove the junit version + test
scope because it's defined in the root pom under dependencyManagement.

b) The Apache Directory API is only used to call on GeneralizedTime, but
this pulls in a large number of dependencies in the distribution. Can we
replace GeneralizedTime with something else, or just pull GeneralizedTime
into our project directly?

c) I think the mysql-backend dependency should be commented out in the
kerby-dist pom.

Colm.

On Mon, Jan 15, 2018 at 2:38 AM, <ze...@apache.org> wrote:

> Repository: directory-kerby
> Updated Branches:
>   refs/heads/trunk 7b30a96ee -> 11089e869
>
>
> DIRKRB-678 Implement MySQL identity backend for KDC server.
>
>
> Project: http://git-wip-us.apache.org/repos/asf/directory-kerby/repo
> Commit: http://git-wip-us.apache.org/repos/asf/directory-kerby/
> commit/11089e86
> Tree: http://git-wip-us.apache.org/repos/asf/directory-kerby/tree/11089e86
> Diff: http://git-wip-us.apache.org/repos/asf/directory-kerby/diff/11089e86
>
> Branch: refs/heads/trunk
> Commit: 11089e86940723fb2ca5850504b93814b4e794ad
> Parents: 7b30a96
> Author: zenglinx <fr...@intel.com>
> Authored: Mon Jan 15 10:38:30 2018 +0800
> Committer: zenglinx <fr...@intel.com>
> Committed: Mon Jan 15 10:38:30 2018 +0800
>
> ----------------------------------------------------------------------
>  kerby-backend/mysql-backend/pom.xml             |  91 ++++
>  .../kdc/identitybackend/MySQLConfKey.java       |  52 +++
>  .../identitybackend/MySQLIdentityBackend.java   | 437 +++++++++++++++++++
>  .../identity/backend/MySQLBackendKdcTest.java   |  78 ++++
>  .../kerb/identity/backend/MySQLBackendTest.java |  57 +++
>  kerby-backend/pom.xml                           |   1 +
>  kerby-dist/kdc-dist/conf/backend.conf           |   4 +
>  kerby-dist/kdc-dist/pom.xml                     |   7 +
>  8 files changed, 727 insertions(+)
> ----------------------------------------------------------------------
>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/pom.xml
> b/kerby-backend/mysql-backend/pom.xml
> new file mode 100644
> index 0000000..d24335a
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/pom.xml
> @@ -0,0 +1,91 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<!--
> +  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. See accompanying LICENSE file.
> +-->
> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="
> http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://
> maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
> +  <modelVersion>4.0.0</modelVersion>
> +
> +  <parent>
> +    <groupId>org.apache.kerby</groupId>
> +    <artifactId>kerby-backend</artifactId>
> +    <version>1.1.1-SNAPSHOT</version>
> +  </parent>
> +
> +  <artifactId>mysql-backend</artifactId>
> +  <name>MySQL identity backend</name>
> +  <description>MySQL identity backend</description>
> +
> +  <dependencies>
> +    <dependency>
> +      <groupId>org.apache.directory.server</groupId>
> +      <artifactId>apacheds-core-api</artifactId>
> +      <version>2.0.0-M23</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>commons-dbutils</groupId>
> +      <artifactId>commons-dbutils</artifactId>
> +      <version>1.6</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.drizzle.jdbc</groupId>
> +      <artifactId>drizzle-jdbc</artifactId>
> +      <version>1.4</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerby-config</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-core</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-identity</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-common</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +    <dependency>
> +      <groupId>junit</groupId>
> +      <artifactId>junit</artifactId>
> +      <version>4.11</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +       <groupId>com.h2database</groupId>
> +       <artifactId>h2</artifactId>
> +       <version>1.4.196</version>
> +       <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-identity-test</artifactId>
> +      <version>${project.version}</version>
> +      <scope>test</scope>
> +    </dependency>
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>kerb-kdc-test</artifactId>
> +      <version>${project.version}</version>
> +      <type>test-jar</type>
> +      <scope>test</scope>
> +    </dependency>
> +  </dependencies>
> +
> +</project>
> \ No newline at end of file
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/main/java/
> org/apache/kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/
> identitybackend/MySQLConfKey.java
> new file mode 100644
> index 0000000..ce2ead6
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLConfKey.java
> @@ -0,0 +1,52 @@
> +/**
> + *  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.kerby.config.ConfigKey;
> +
> +/**
> + * Define all the MySQL backend related configuration items with default
> values.
> + */
> +public enum MySQLConfKey implements ConfigKey {
> +    MYSQL_DRIVER("org.drizzle.jdbc.DrizzleDriver"),
> +    MYSQL_URL("jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend"),
> +    MYSQL_USER("root"),
> +    MYSQL_PASSWORD("passwd");
> +
> +    private Object defaultValue;
> +
> +    MySQLConfKey() {
> +        this.defaultValue = null;
> +    }
> +
> +    MySQLConfKey(Object defaultValue) {
> +        this.defaultValue = defaultValue;
> +    }
> +
> +    @Override
> +    public String getPropertyKey() {
> +        return name().toLowerCase();
> +    }
> +
> +    @Override
> +    public Object getDefaultValue() {
> +        return this.defaultValue;
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/main/java/
> org/apache/kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
> b/kerby-backend/mysql-backend/src/main/java/org/apache/kerby/kerberos/kdc/
> identitybackend/MySQLIdentityBackend.java
> new file mode 100644
> index 0000000..32ede90
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/main/java/org/apache/
> kerby/kerberos/kdc/identitybackend/MySQLIdentityBackend.java
> @@ -0,0 +1,437 @@
> +/**
> + *  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.commons.dbutils.DbUtils;
> +import org.apache.directory.api.util.GeneralizedTime;
> +import org.apache.kerby.config.Config;
> +import org.apache.kerby.kerberos.kerb.KrbException;
> +import org.apache.kerby.kerberos.kerb.identity.backend.
> AbstractIdentityBackend;
> +import org.apache.kerby.kerberos.kerb.request.KrbIdentity;
> +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.sql.Connection;
> +import java.sql.DriverManager;
> +import java.sql.PreparedStatement;
> +import java.sql.ResultSet;
> +import java.sql.SQLException;
> +import java.text.ParseException;
> +import java.util.ArrayList;
> +import java.util.List;
> +import java.util.Map;
> +import javax.sql.rowset.serial.SerialBlob;
> +
> +/**
> + * A MySQL based backend implementation.
> + */
> +public class MySQLIdentityBackend extends AbstractIdentityBackend {
> +    private Connection connection;
> +    private String driver;
> +    private String url;
> +    private String user;
> +    private String password;
> +    private static final Logger LOG = LoggerFactory.getLogger(
> MySQLIdentityBackend.class);
> +    private String identityTable;
> +    private String keyInfoTable;
> +
> +    /**
> +     * Constructing an instance using specified config that contains
> anything
> +     * to be used to initialize an MySQL Backend.
> +     * @param config . The config is used to config the backend.
> +     */
> +    public MySQLIdentityBackend(final Config config) {
> +        setConfig(config);
> +    }
> +
> +    public MySQLIdentityBackend() { }
> +
> +    /**
> +     * Start the MySQL connection.
> +     */
> +    private void startConnection() throws KrbException {
> +        try {
> +            Class.forName(driver);
> +            connection = DriverManager.getConnection(url, user,
> password);
> +            if (!connection.isClosed()) {
> +                LOG.info("Succeeded in connecting to MySQL.");
> +            }
> +        } catch (ClassNotFoundException e) {
> +            throw new KrbException("JDBC Driver Class not found. ", e);
> +        } catch (SQLException e) {
> +            throw new KrbException("Failed to connecting to MySQL. ", e);
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doInitialize() throws KrbException {
> +        LOG.info("Initializing the MySQL identity backend.");
> +        driver = getConfig().getString(MySQLConfKey.MYSQL_DRIVER, true);
> +        user = getConfig().getString(MySQLConfKey.MYSQL_USER, true);
> +        password = getConfig().getString(MySQLConfKey.MYSQL_PASSWORD,
> true);
> +
> +        String urlString = getConfig().getString(MySQLConfKey.MYSQL_URL,
> true);
> +        if (urlString == null || urlString.isEmpty()) {
> +            urlString = getBackendConfig().getString(MySQLConfKey.MYSQL_URL,
> true);
> +        }
> +        url = urlString;
> +
> +        ResultSet resCheckTable = null;
> +        PreparedStatement preInitialize = null;
> +        PreparedStatement preKdcRealm = null;
> +        ResultSet resKdcRealm = null;
> +        PreparedStatement preIdentity = null;
> +        PreparedStatement preKey = null;
> +        try {
> +            startConnection();
> +
> +            resCheckTable = connection.getMetaData().getTables(null,
> null, "kdc_config", null);
> +            if (resCheckTable.next()) {
> +                // Set initialized for kdc config table if HAS enabled
> +                String stmInitialize = "UPDATE `kdc_config` SET
> initialized = true WHERE id = 1";
> +                preInitialize = connection.prepareStatement(
> stmInitialize);
> +                preInitialize.executeUpdate();
> +
> +                // Get identity table name according to realm of kdc
> +                String stmKdcRealm = "SELECT realm FROM `kdc_config`";
> +                preKdcRealm = connection.prepareStatement(stmKdcRealm);
> +                resKdcRealm = preKdcRealm.executeQuery();
> +                if (resKdcRealm.next()) {
> +                    String realm = resKdcRealm.getString("realm")
> .toLowerCase();
> +                    identityTable = "`" + realm + "_identity" + "`";
> +                    keyInfoTable = "`" + realm + "_key" + "`";
> +                } else {
> +                    throw new KrbException("Failed to get kdc config.");
> +                }
> +            } else {
> +                identityTable = "`" + "kerby_identity" + "`";
> +                keyInfoTable = "`" + "kerby_key" + "`";
> +            }
> +
> +            // Create identity table
> +            String stmIdentity = "CREATE TABLE IF NOT EXISTS " +
> identityTable
> +                + " (principal varchar(255) NOT NULL, key_version INTEGER
> "
> +                + "DEFAULT 1, kdc_flags INTEGER DEFAULT 0, disabled bool "
> +                + "DEFAULT NULL, locked bool DEFAULT NULL, created_time "
> +                + "VARCHAR(255) DEFAULT NULL, expire_time VARCHAR(255) "
> +                + "DEFAULT NULL, PRIMARY KEY (principal) ) ENGINE=INNODB "
> +                + "DEFAULT CHARSET=utf8;";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.executeUpdate();
> +
> +            // Create key table
> +            String stmKey = "CREATE TABLE IF NOT EXISTS " + keyInfoTable
> +                + " (key_id INTEGER NOT NULL AUTO_INCREMENT, key_type "
> +                + "VARCHAR(255) DEFAULT NULL, kvno INTEGER DEFAULT -1, "
> +                + "key_value BLOB DEFAULT NULL, principal VARCHAR(255)
> NOT NULL,"
> +                + "PRIMARY KEY (key_id), INDEX (principal), FOREIGN KEY "
> +                + "(principal) REFERENCES " + identityTable +
> "(principal) "
> +                + ") ENGINE=INNODB DEFAULT CHARSET=utf8;";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.executeUpdate();
> +
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while initialize MySQL backend." +
> e.toString());
> +            throw new KrbException("Failed to create table in database.
> ", e);
> +        } finally {
> +            DbUtils.closeQuietly(resCheckTable);
> +            DbUtils.closeQuietly(preInitialize);
> +            DbUtils.closeQuietly(preKdcRealm);
> +            DbUtils.closeQuietly(resKdcRealm);
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doStop() throws KrbException {
> +        try {
> +            closeConnection();
> +            if (connection.isClosed()) {
> +                LOG.info("Succeeded in closing connection with MySQL.");
> +            }
> +        } catch (SQLException e) {
> +            LOG.error("Failed to close connection with MySQL.");
> +            throw new KrbException("Failed to close connection with
> MySQL. ", e);
> +        }
> +    }
> +
> +    /**
> +     * Close the connection for stop().
> +     * @throws SQLException if SQLException handled
> +     */
> +    private void closeConnection() throws SQLException {
> +        if (!connection.isClosed()) {
> +            connection.close();
> +        }
> +    }
> +
> +    /**
> +     * Convert a KerberosTime type object to a generalized time form of
> String.
> +     * @param kerberosTime The kerberos time to convert
> +     */
> +    private String toGeneralizedTime(final KerberosTime kerberosTime) {
> +        GeneralizedTime generalizedTime = new
> GeneralizedTime(kerberosTime.getValue());
> +        return generalizedTime.toString();
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doAddIdentity(KrbIdentity identity) throws
> KrbException {
> +        String principalName = identity.getPrincipalName();
> +        int keyVersion = identity.getKeyVersion();
> +        int kdcFlags = identity.getKdcFlags();
> +        boolean disabled = identity.isDisabled();
> +        boolean locked = identity.isLocked();
> +        String createdTime = toGeneralizedTime(identity.
> getCreatedTime());
> +        String expireTime = toGeneralizedTime(identity.getExpireTime());
> +        Map<EncryptionType, EncryptionKey> keys = identity.getKeys();
> +
> +        PreparedStatement preIdentity = null;
> +        PreparedStatement preKey = null;
> +
> +        KrbIdentity duplicateIdentity = doGetIdentity(principalName);
> +        if (duplicateIdentity != null) {
> +            LOG.warn("The identity maybe duplicate.");
> +
> +            return duplicateIdentity;
> +        } else {
> +            try {
> +                startConnection();
> +                connection.setAutoCommit(false);
> +
> +                // Insert identity to identity table
> +                String stmIdentity = "insert into " + identityTable
> +                        + " (principal, key_version, kdc_flags, disabled,
> locked, created_time, expire_time)"
> +                        + " values(?, ?, ?, ?, ?, ?, ?)";
> +                preIdentity = connection.prepareStatement(stmIdentity);
> +                preIdentity.setString(1, principalName);
> +                preIdentity.setInt(2, keyVersion);
> +                preIdentity.setInt(3, kdcFlags);
> +                preIdentity.setBoolean(4, disabled);
> +                preIdentity.setBoolean(5, locked);
> +                preIdentity.setString(6, createdTime);
> +                preIdentity.setString(7, expireTime);
> +                preIdentity.executeUpdate();
> +
> +                // Insert keys to key table
> +                for (Map.Entry<EncryptionType, EncryptionKey> entry :
> keys.entrySet()) {
> +                    String stmKey = "insert into " + keyInfoTable + "
> (key_type, kvno, key_value, principal)"
> +                        + " values(?, ?, ?, ?)";
> +                    preKey = connection.prepareStatement(stmKey);
> +                    preKey.setString(1, entry.getKey().getName());
> +                    preKey.setInt(2, entry.getValue().getKvno());
> +                    preKey.setBlob(3, new SerialBlob(entry.getValue().
> getKeyData()));
> +                    preKey.setString(4, principalName);
> +                    preKey.executeUpdate();
> +                }
> +
> +                connection.commit();
> +                return identity;
> +            } catch (SQLException e) {
> +                try {
> +                    LOG.info("Transaction is being rolled back.");
> +                    connection.rollback();
> +                } catch (SQLException ex) {
> +                    throw new KrbException("Transaction roll back failed.
> ", ex);
> +                }
> +                LOG.error("Error occurred while adding identity.");
> +                throw new KrbException("Failed to add identity. ", e);
> +            } finally {
> +                DbUtils.closeQuietly(preIdentity);
> +                DbUtils.closeQuietly(preKey);
> +                doStop();
> +            }
> +        }
> +    }
> +
> +    /**
> +     * Create kerberos time.
> +     * @param generalizedTime generalized time
> +     * @throws ParseException parse exception
> +     */
> +    private KerberosTime createKerberosTime(final String generalizedTime)
> throws ParseException {
> +        long time = new GeneralizedTime(generalizedTime).getTime();
> +        return new KerberosTime(time);
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doGetIdentity(final String principalName)
> throws KrbException {
> +        KrbIdentity krbIdentity = new KrbIdentity(principalName);
> +
> +        PreparedStatement preIdentity = null;
> +        ResultSet resIdentity = null;
> +        PreparedStatement preKey = null;
> +        ResultSet resKey = null;
> +        try {
> +            startConnection();
> +
> +            // Get identity from identity table
> +            String stmIdentity = "SELECT * FROM " + identityTable + "
> where principal = ?";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.setString(1, principalName);
> +            resIdentity = preIdentity.executeQuery();
> +
> +            if (!resIdentity.isBeforeFirst()) {
> +                return null;
> +            }
> +
> +            while (resIdentity.next()) {
> +                krbIdentity.setKeyVersion(resIdentity.getInt("key_
> version"));
> +                krbIdentity.setKdcFlags(resIdentity.getInt("kdc_flags"));
> +                krbIdentity.setDisabled(resIdentity.getBoolean("
> disabled"));
> +                krbIdentity.setLocked(resIdentity.getBoolean("locked"));
> +                krbIdentity.setCreatedTime(createKerberosTime(
> resIdentity.getString("created_time")));
> +                krbIdentity.setExpireTime(createKerberosTime(
> resIdentity.getString("expire_time")));
> +            }
> +
> +            // Get keys from key table
> +            List<EncryptionKey> keys = new ArrayList<>();
> +            String stmKey = "SELECT * FROM " + keyInfoTable + " where
> principal = ?";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.setString(1, principalName);
> +            resKey = preKey.executeQuery();
> +            while (resKey.next()) {
> +                int kvno = resKey.getInt("kvno");
> +                String keyType = resKey.getString("key_type");
> +                EncryptionType eType = EncryptionType.fromName(keyType);
> +                byte[] keyValue = resKey.getBytes("key_value");
> +                EncryptionKey key = new EncryptionKey(eType, keyValue,
> kvno);
> +                keys.add(key);
> +            }
> +
> +            krbIdentity.addKeys(keys);
> +            return krbIdentity;
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while getting identity.");
> +            throw new KrbException("Failed to get identity. ", e);
> +        } catch (ParseException e) {
> +            throw new KrbException("Failed to get identity. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(resIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            DbUtils.closeQuietly(resKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected KrbIdentity doUpdateIdentity(KrbIdentity identity) throws
> KrbException {
> +        String principalName = identity.getPrincipalName();
> +        try {
> +            doDeleteIdentity(principalName); // Delete former identity
> +            doAddIdentity(identity); // Insert new identity
> +        } catch (KrbException e) {
> +            LOG.error("Error occurred while updating identity: " +
> principalName);
> +            throw new KrbException("Failed to update identity. ", e);
> +        }
> +
> +        return getIdentity(principalName);
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected void doDeleteIdentity(String principalName) throws
> KrbException {
> +        PreparedStatement preKey = null;
> +        PreparedStatement preIdentity = null;
> +        try {
> +            startConnection();
> +            connection.setAutoCommit(false);
> +
> +            // Delete keys from key table
> +            String stmKey = "DELETE FROM  " + keyInfoTable + " where
> principal = ?";
> +            preKey = connection.prepareStatement(stmKey);
> +            preKey.setString(1, principalName);
> +            preKey.executeUpdate();
> +
> +            // Dlete identity from identity table
> +            String stmIdentity = "DELETE FROM " + identityTable + " where
> principal = ? ";
> +            preIdentity = connection.prepareStatement(stmIdentity);
> +            preIdentity.setString(1, principalName);
> +            preIdentity.executeUpdate();
> +
> +            connection.commit();
> +        } catch (SQLException e) {
> +            try {
> +                LOG.info("Transaction is being rolled back.");
> +                connection.rollback();
> +            } catch (SQLException ex) {
> +                throw new KrbException("Transaction roll back failed. ",
> ex);
> +            }
> +            LOG.error("Error occurred while deleting identity.");
> +            throw new KrbException("Failed to delete identity. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preIdentity);
> +            DbUtils.closeQuietly(preKey);
> +            doStop();
> +        }
> +    }
> +
> +    /**
> +     * {@inheritDoc}
> +     */
> +    @Override
> +    protected Iterable<String> doGetIdentities() throws KrbException {
> +        List<String> identityNames = new ArrayList<>();
> +        PreparedStatement preSmt = null;
> +        ResultSet result = null;
> +        try {
> +            startConnection();
> +            String statement = "SELECT * FROM " + identityTable;
> +            preSmt = connection.prepareStatement(statement);
> +            result = preSmt.executeQuery();
> +            while (result.next()) {
> +                identityNames.add(result.getString("principal"));
> +            }
> +            result.close();
> +            preSmt.close();
> +        } catch (SQLException e) {
> +            LOG.error("Error occurred while getting identities.");
> +            throw new KrbException("Failed to get identities. ", e);
> +        } finally {
> +            DbUtils.closeQuietly(preSmt);
> +            DbUtils.closeQuietly(result);
> +            doStop();
> +        }
> +
> +        return identityNames;
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/test/java/
> org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> new file mode 100644
> index 0000000..565a0ee
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendKdcTest.java
> @@ -0,0 +1,78 @@
> +/**
> + *  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.kerb.identity.backend;
> +
> +import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
> +import org.apache.kerby.kerberos.kerb.KrbException;
> +import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
> +import org.apache.kerby.kerberos.kerb.server.KdcTestBase;
> +import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
> +import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
> +import org.junit.After;
> +import org.junit.Assert;
> +import org.junit.Test;
> +
> +import java.io.File;
> +
> +import static org.assertj.core.api.Assertions.assertThat;
> +
> +public class MySQLBackendKdcTest extends KdcTestBase {
> +    private static File testDir = new File(System.getProperty("test.dir",
> "target"));
> +    private static File dbFile = new File(testDir, "mysqlbackend.mv.db");
> +
> +    @After
> +    public void tearDown() {
> +        if (dbFile.exists() && !dbFile.delete()) {
> +            System.err.println("Failed to delete the test database
> file.");
> +        }
> +    }
> +
> +    @Override
> +    protected void prepareKdc() throws KrbException {
> +        BackendConfig backendConfig = getKdcServer().getBackendConfig();
> +
> +        backendConfig.setString(KdcConfigKey.KDC_IDENTITY_BACKEND,
> +                "org.apache.kerby.kerberos.kdc.identitybackend.
> MySQLIdentityBackend");
> +        backendConfig.setString(MySQLConfKey.MYSQL_DRIVER,
> "org.h2.Driver");
> +        backendConfig.setString(MySQLConfKey.MYSQL_URL,
> +                "jdbc:h2:" + testDir.getAbsolutePath() +
> "/mysqlbackend;MODE=MySQL");
> +        backendConfig.setString(MySQLConfKey.MYSQL_USER, "root");
> +        backendConfig.setString(MySQLConfKey.MYSQL_PASSWORD, "123456");
> +
> +        super.prepareKdc();
> +    }
> +
> +    @Test
> +    public void testKdc() {
> +        TgtTicket tgt;
> +        SgtTicket tkt;
> +
> +        try {
> +            tgt = getKrbClient().requestTgt(getClientPrincipal(),
> getClientPassword());
> +            assertThat(tgt).isNotNull();
> +
> +            tkt = getKrbClient().requestSgt(tgt, getServerPrincipal());
> +            assertThat(tkt).isNotNull();
> +        } catch (Exception e) {
> +            Assert.fail("Exception occurred with good password. "
> +                    + e.toString());
> +        }
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/mysql-backend/src/test/java/
> org/apache/kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> new file mode 100644
> index 0000000..59f3500
> --- /dev/null
> +++ b/kerby-backend/mysql-backend/src/test/java/org/apache/
> kerby/kerberos/kerb/identity/backend/MySQLBackendTest.java
> @@ -0,0 +1,57 @@
> +/**
> + *  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.kerb.identity.backend;
> +
> +import org.apache.kerby.config.Conf;
> +import org.apache.kerby.kerberos.kdc.identitybackend.MySQLConfKey;
> +import org.apache.kerby.kerberos.kdc.identitybackend.
> MySQLIdentityBackend;
> +import org.apache.kerby.kerberos.kerb.KrbException;
> +import org.junit.AfterClass;
> +import org.junit.BeforeClass;
> +
> +import java.io.File;
> +import java.io.IOException;
> +
> +public class MySQLBackendTest extends BackendTestBase {
> +    private static File testDir = new File(System.getProperty("test.dir",
> "target"));
> +    private static File dbFile = new File(testDir, "mysqlbackend.mv.db");
> +
> +    @BeforeClass
> +    public static void setup() throws KrbException, IOException {
> +        Conf config = new Conf();
> +        config.setString(MySQLConfKey.MYSQL_DRIVER, "org.h2.Driver");
> +        config.setString(MySQLConfKey.MYSQL_URL,
> +                "jdbc:h2:" + testDir.getCanonicalPath() +
> "/mysqlbackend;MODE=MySQL");
> +        config.setString(MySQLConfKey.MYSQL_USER, "root");
> +        config.setString(MySQLConfKey.MYSQL_PASSWORD, "123456");
> +        backend = new MySQLIdentityBackend(config);
> +        backend.initialize();
> +    }
> +
> +    @AfterClass
> +    public static void tearDown() throws KrbException {
> +        if (backend != null) {
> +            backend.stop();
> +        }
> +        if (dbFile.exists() && !dbFile.delete()) {
> +            System.err.println("Failed to delete the test database
> file.");
> +        }
> +    }
> +}
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-backend/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-backend/pom.xml b/kerby-backend/pom.xml
> index ef95b87..6ce432c 100644
> --- a/kerby-backend/pom.xml
> +++ b/kerby-backend/pom.xml
> @@ -53,6 +53,7 @@
>          <module>ldap-backend</module>
>          <module>mavibot-backend</module>
>          <module>zookeeper-backend</module>
> +        <module>mysql-backend</module>
>        </modules>
>      </profile>
>    </profiles>
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-dist/kdc-dist/conf/backend.conf
> ----------------------------------------------------------------------
> diff --git a/kerby-dist/kdc-dist/conf/backend.conf
> b/kerby-dist/kdc-dist/conf/backend.conf
> index 2ead268..20134ef 100644
> --- a/kerby-dist/kdc-dist/conf/backend.conf
> +++ b/kerby-dist/kdc-dist/conf/backend.conf
> @@ -22,3 +22,7 @@ embedded_zk = false
>  zk_host = 127.0.0.1
>  zk_port = 2181
>  data_dir = /tmp/zookeeper/data
> +mysql_driver = org.drizzle.jdbc.DrizzleDriver
> +mysql_url = jdbc:mysql:thin://127.0.0.1:3306/mysqlbackend?createDB=true
> +mysql_user = root
> +mysql_password = passwd
>
> http://git-wip-us.apache.org/repos/asf/directory-kerby/
> blob/11089e86/kerby-dist/kdc-dist/pom.xml
> ----------------------------------------------------------------------
> diff --git a/kerby-dist/kdc-dist/pom.xml b/kerby-dist/kdc-dist/pom.xml
> index 1766d28..3a5de2d 100644
> --- a/kerby-dist/kdc-dist/pom.xml
> +++ b/kerby-dist/kdc-dist/pom.xml
> @@ -89,6 +89,13 @@
>        <version>${gson.version}</version>
>      </dependency>
>
> +    <!-- For mysql backend -->
> +    <dependency>
> +      <groupId>org.apache.kerby</groupId>
> +      <artifactId>mysql-backend</artifactId>
> +      <version>${project.version}</version>
> +    </dependency>
> +
>      <!-- For common and misc -->
>      <dependency>
>        <groupId>org.slf4j</groupId>
>
>


-- 
Colm O hEigeartaigh

Talend Community Coder
http://coders.talend.com