DIRKRB-499 A complete krb5 conf file parser. Contributed by Yan.
diff --git a/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
new file mode 100644
index 0000000..a7654dc
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/main/java/org/apache/kerby/kerberos/kerb/common/Krb5Parser.java
@@ -0,0 +1,216 @@
+/**
+ * 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.common;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A parser to parse krb5.conf format file.
+ */
+public class Krb5Parser {
+ private File krb5conf;
+ /**
+ * The variable items regards section name as a key of Map, and
+ * contents of a section as a value with Object type.
+ * In specific, the value is a recursive Map type, which can be
+ * in the form of both Map<String, String> and Map<String, Object>,
+ * depending on contents of the section.
+ */
+ private Map<String, Object> items;
+
+ public Krb5Parser(File confFile) {
+ krb5conf = confFile;
+ items = null;
+ }
+
+ /**
+ * Load the krb5.conf into a member variable, which is a Map.
+ * @throws IOException
+ */
+ public void load() throws IOException {
+ BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(krb5conf)));
+ items = new HashMap<String, Object>();
+
+ String originLine = br.readLine();
+ while (originLine != null) {
+ String line = originLine.trim();
+ /*parse through comments*/
+ if (line.startsWith("#")) {
+ originLine = br.readLine();
+ } else if (line.startsWith("[")) {
+ insertSections(line, br, items);
+ originLine = br.readLine();
+ } else {
+ throw new RuntimeException("Unable to parse:" + originLine);
+ }
+ }
+ br.close();
+ }
+
+ /**
+ * Get all the names of sections in a list.
+ * @return a list of section names.
+ */
+ public List<String> getSections() {
+ List<String> al = new ArrayList<String>(items.keySet());
+ return al;
+ }
+
+ /**
+ * Get the contents of a section given the section name.
+ * @param sectionName the name of a section
+ * @return a Map of section contents
+ */
+ public Map<String, Object> getSection(String sectionName) {
+ Map<String, Object> sections = (HashMap) items.get(sectionName);
+ return sections;
+ }
+
+ /**
+ * Print all the meaningful contents of the krb5.conf on the console.
+ * Comments are ignored.
+ * Hierarchy is considered.
+ * Attention that the order of sections and the order inside a section
+ * will be different to the original file, due to the use of HashMap.
+ */
+ public void dump() {
+ printSection(items);
+ }
+
+ private void insertSections(String line, BufferedReader br, Map<String, Object> items) throws IOException {
+ while (line.startsWith("[")) {
+ String sectionName = line.substring(1, line.length() - 1);
+ Map<String, Object> entries = new HashMap<String, Object>();
+ line = br.readLine();
+ if (line != null) {
+ line = line.trim();
+ line = insertEntries(line, br, entries);
+ items.put(sectionName, entries);
+ }
+ /*line has been modified after the recursive.*/
+ if (line == null) {
+ /*the end of file*/
+ break;
+ }
+ }
+ }
+
+ /**
+ * recursively go through the key-value pairs of a section
+ * */
+ private String insertEntries(String line, BufferedReader br, Map<String, Object> entries) throws IOException {
+ if (line == null) {
+ return line;
+ }
+ if (line.startsWith("[")) {
+ return line;
+ }
+ if (line.startsWith("}")) {
+ line = br.readLine();
+ if (line != null) {
+ line = line.trim();
+ }
+ return line;
+ }
+ if (line.length() == 0) {
+ line = br.readLine();
+ if (line != null) {
+ line = line.trim();
+ line = insertEntries(line, br, entries);
+ }
+ return line;
+ }
+ /*some special cases above*/
+ String[] kv = line.split("=");
+ if (kv.length > 2) {
+ throw new RuntimeException("Unable to parse:" + line);
+ }
+ kv[0] = kv[0].trim();
+ kv[1] = kv[1].trim();
+
+ if (kv[1].startsWith("{")) {
+ Map<String, Object> meValue = new HashMap<String, Object>();
+ line = br.readLine();
+ if (line != null) {
+ line = line.trim();
+ line = insertEntries(line, br, meValue);
+ entries.put(kv[0], meValue);
+ line = insertEntries(line, br, entries);
+ }
+ } else {
+ entries.put(kv[0], kv[1]);
+ line = br.readLine();
+ if (line != null) {
+ line = line.trim();
+ line = insertEntries(line, br, entries);
+ }
+ }
+ return line;
+ }
+
+ private void printSection(Map<String, Object> map) {
+ Iterator iter = map.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = (String) entry.getKey();
+ Object value = entry.getValue();
+ System.out.println("[" + key + "]");
+
+ if (value instanceof Map) {
+ int count = 0;
+ printEntry((Map) value, count);
+ } else {
+ throw new RuntimeException("Unable to print contents of [" + key + "]");
+ }
+ }
+ }
+
+ private void printEntry(Map<String, Object> map, int count) {
+ Iterator iter = map.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ String key = (String) entry.getKey();
+ Object value = entry.getValue();
+ for (int i = 0; i < count; i++) {
+ System.out.print("\t");
+ }
+ if (value instanceof String) {
+ System.out.println(key + " = " + (String) value);
+ }
+ if (value instanceof Map) {
+ System.out.println(key + " = {");
+ printEntry((Map) value, count + 1);
+ for (int i = 0; i < count; i++) {
+ System.out.print("\t");
+ }
+ System.out.println("}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
new file mode 100644
index 0000000..b11ad16
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/test/java/org/apache/kerby/kerberos/kerb/Krb5ParserTest.java
@@ -0,0 +1,55 @@
+/**
+ * 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;
+
+import org.apache.kerby.kerberos.kerb.common.Krb5Parser;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A unit test for Krb5Parser.
+ * Checked the number of sections, the name of
+ * sections, and contents of a section.
+ */
+public class Krb5ParserTest {
+
+ @Test
+ public void test() throws IOException {
+ URL url = Krb5ParserTest.class.getResource("/krb5.conf");
+ Krb5Parser k = new Krb5Parser(new File(url.getFile()));
+ k.load();
+
+ assertThat(k.getSections().size()).isEqualTo(4);
+ assertThat(k.getSections().contains("libdefaults")).isTrue();
+
+ assertThat(k.getSection("libdefaults").get("dns_lookup_kdc")).isEqualTo("false");
+ assertThat(k.getSection("realms").get("ATHENA.MIT.EDU") instanceof Map).isTrue();
+ Map<String, Object> m1 = (Map) k.getSection("realms").get("ATHENA.MIT.EDU");
+ assertThat(m1.get("v4_instance_convert") instanceof Map).isTrue();
+ Map<String, Object> m2 = (Map) m1.get("v4_instance_convert");
+ assertThat(m2.get("mit")).isEqualTo("mit.edu");
+ }
+}
diff --git a/kerby-kerb/kerb-common/src/test/resources/krb5.conf b/kerby-kerb/kerb-common/src/test/resources/krb5.conf
new file mode 100644
index 0000000..1a5b35e
--- /dev/null
+++ b/kerby-kerb/kerb-common/src/test/resources/krb5.conf
@@ -0,0 +1,59 @@
+#
+# 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.
+#
+[libdefaults]
+ default_realm = KRB.COM
+ kdc_host = kdc-server.example.com
+ kdc_realm = TEST.COM
+ dns_lookup_kdc = false
+ dns_lookup_realm = false
+ allow_weak_crypto = true
+ ticket_lifetime = 86400
+ renew_lifetime = 604800
+ forwardable = true
+ permitted_enctypes = des-cbc-crc aes128-cts-hmac-sha1-96
+ clockskew = 300
+ proxiable = true
+ default_tgs_enctypes = des-cbc-crc
+ default_tkt_enctypes = des-cbc-crc
+[realms]
+ ATHENA.MIT.EDU = {
+ admin_server = KERBEROS.MIT.EDU
+ default_domain = MIT.EDU
+ v4_instance_convert = {
+ mit = mit.edu
+ lithium = lithium.lcs.mit.edu
+ }
+ }
+ ANDREW.CMU.EDU = {
+ admin_server = vice28.fs.andrew.cmu.edu
+ }
+ GNU.ORG = {
+ kdc = kerberos.gnu.org
+ kdc = kerberos-2.gnu.org
+ admin_server = kerberos.gnu.org
+ }
+[domain_realm]
+ .mit.edu = ATHENA.MIT.EDU
+ mit.edu = ATHENA.MIT.EDU
+ .media.mit.edu = MEDIA-LAB.MIT.EDU
+ media.mit.edu = MEDIA-LAB.MIT.EDU
+ .ucsc.edu = CATS.UCSC.EDU
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
\ No newline at end of file