]> source.dussan.org Git - gitblit.git/commitdiff
Mostly functional Gitblit Certificate Authority tool
authorJames Moger <james.moger@gitblit.com>
Fri, 23 Nov 2012 17:01:47 +0000 (12:01 -0500)
committerJames Moger <james.moger@gitblit.com>
Fri, 23 Nov 2012 17:01:47 +0000 (12:01 -0500)
20 files changed:
build.xml
docs/04_releases.mkd
resources/bullet_delete.png [new file with mode: 0644]
resources/bullet_key.png [new file with mode: 0644]
resources/rosette_16x16.png [new file with mode: 0644]
resources/vcard_16x16.png [new file with mode: 0644]
src/com/gitblit/authority/CertificateStatus.java [new file with mode: 0644]
src/com/gitblit/authority/CertificateStatusRenderer.java [new file with mode: 0644]
src/com/gitblit/authority/CertificatesTableModel.java [new file with mode: 0644]
src/com/gitblit/authority/GitblitAuthority.java
src/com/gitblit/authority/GitblitAuthorityLauncher.java [new file with mode: 0644]
src/com/gitblit/authority/NewClientCertificateDialog.java [new file with mode: 0644]
src/com/gitblit/authority/UserCertificatePanel.java [new file with mode: 0644]
src/com/gitblit/authority/UserCertificateTableModel.java [new file with mode: 0644]
src/com/gitblit/authority/Utils.java [new file with mode: 0644]
src/com/gitblit/authority/X509CertificateViewer.java [new file with mode: 0644]
src/com/gitblit/client/DateCellRenderer.java
src/com/gitblit/utils/StringUtils.java
src/com/gitblit/utils/TimeUtils.java
src/com/gitblit/wicket/GitBlitWebApp.properties

index 7c5e8cc5a98608da92fb566694f43667aa205373..8a7fd54b2284d1af0af41f5994701dd4350258ba 100644 (file)
--- a/build.xml
+++ b/build.xml
@@ -92,6 +92,7 @@
                <property name="distribution.pomfile" value="${basedir}/pom.xml" />\r
                <property name="fedclient.zipfile" value="fedclient-${gb.version}.zip" />\r
                <property name="manager.zipfile" value="manager-${gb.version}.zip" />\r
+               <property name="authority.zipfile" value="authority-${gb.version}.zip" />\r
                <property name="gbapi.zipfile" value="gbapi-${gb.version}.zip" />\r
                <property name="express.zipfile" value="express-${gb.version}.zip" />\r
        </target>\r
                <copy todir="${basedir}/certs" overwrite="false">\r
                        <fileset dir="${basedir}/distrib">\r
                                <include name="authority.conf" />\r
+                               <include name="*.tmpl" />\r
                        </fileset>\r
                </copy>\r
 \r
                                <include name="authority.conf" />\r
                        </fileset>\r
                </copy>\r
-\r
+                                               \r
                <!-- Build jar -->\r
                <jar jarfile="${project.deploy.dir}/${project.jar}">\r
                        <fileset dir="${project.build.dir}">\r
                                <include name="**/*" />\r
-                               <exclude name="com/gitblit/client/**" />\r
+                               <exclude name="com/gitblit/client/**" />                                \r
                        </fileset>\r
                        <fileset dir="${project.resources.dir}">\r
                                <exclude name="thumbs.db" />\r
                                <exclude name="com/gitblit/GitBlitServer*.class" />\r
                                <exclude name="com/gitblit/Launcher*.class" />\r
                                <exclude name="com/gitblit/MakeCertificate*.class" />\r
+                               <exclude name="com/gitblit/authority/**" />\r
                        </fileset>\r
                </copy>\r
 \r
                                <exclude name="com/gitblit/tests/" />\r
                                <exclude name="com/gitblit/build/**" />\r
                                <exclude name="com/gitblit/client/**" />\r
+                               <exclude name="com/gitblit/authority/**" />\r
                                <exclude name="com/gitblit/AddIndexedBranch*.class" />\r
                                <exclude name="com/gitblit/GitBlitServer*.class" />\r
                                <exclude name="com/gitblit/Launcher*.class" />\r
                                <exclude name="com/gitblit/GitBlitServer*.class" />\r
                                <exclude name="com/gitblit/Launcher*.class" />\r
                                <exclude name="com/gitblit/MakeCertificate*.class" />\r
+                               <exclude name="com/gitblit/authority/**" />\r
                        </fileset>\r
                </jar>\r
 \r
                </zip>\r
        </target>\r
        \r
+       \r
+       <!-- \r
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
+               Build the stand-alone, Gitblit Authority\r
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
+       -->\r
+       <target name="buildAuthority" depends="compile" description="Builds the stand-alone Gitblit Authority">\r
+               <echo>Building Gitblit Authority ${gb.version}</echo>\r
+\r
+               <genjar jarfile="authority-${gb.version}.jar">\r
+                       <resource file="${basedir}/src/com/gitblit/client/splash.png" />\r
+                       <resource file="${basedir}/resources/gitblt-favicon.png" />\r
+                       <resource file="${basedir}/resources/user_16x16.png" />\r
+                       <resource file="${basedir}/resources/users_16x16.png" />\r
+                       <resource file="${basedir}/resources/rosette_16x16.png" />\r
+                       <resource file="${basedir}/resources/vcard_16x16.png" />\r
+                       <resource file="${basedir}/resources/settings_16x16.png" />\r
+                       <resource file="${basedir}/resources/search-icon.png" />\r
+                       <resource file="${basedir}/resources/blank.png" />\r
+                       <resource file="${basedir}/resources/bullet_green.png" />\r
+                       <resource file="${basedir}/resources/bullet_orange.png" />\r
+                       <resource file="${basedir}/resources/bullet_red.png" />\r
+                       <resource file="${basedir}/resources/bullet_white.png" />\r
+                       <resource file="${basedir}/resources/bullet_delete.png" />\r
+                       <resource file="${basedir}/resources/bullet_key.png" />\r
+                       <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp.properties" />\r
+                       <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_es.properties" />\r
+                       <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_ja.properties" />\r
+                       <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_ko.properties" />\r
+                       <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_pl.properties" />\r
+\r
+                       <class name="com.gitblit.authority.GitblitAuthorityLauncher" />\r
+                       <classfilter>\r
+                               <exclude name="org.apache." />\r
+                               <exclude name="org.bouncycastle." />\r
+                               <exclude name="org.eclipse." />\r
+                               <exclude name="org.slf4j." />\r
+                               <exclude name="com.beust." />\r
+                               <exclude name="com.google." />\r
+                               <exclude name="com.unboundid." />\r
+                       </classfilter>\r
+                       <classpath refid="master-classpath" />\r
+                       <manifest>\r
+                               <attribute name="Main-Class" value="com.gitblit.authority.GitblitAuthorityLauncher" />\r
+                               <attribute name="SplashScreen-Image" value="splash.png" />\r
+                               <attribute name="Specification-Version" value="${gb.version}" />\r
+                               <attribute name="Release-Date" value="${gb.versionDate}" />\r
+                       </manifest>\r
+               </genjar>\r
+\r
+               <!-- Build Authority Zip file -->\r
+               <zip destfile="${authority.zipfile}">\r
+                       <fileset dir="${basedir}">\r
+                               <include name="authority-${gb.version}.jar" />\r
+                               <include name="LICENSE" />\r
+                               <include name="NOTICE" />\r
+                       </fileset>\r
+                       <zipfileset dir="${basedir}/distrib" prefix="certs">\r
+                               <include name="authority.conf" />\r
+                               <include name="mail.tmpl" />\r
+                               <include name="instructions.tmpl" />\r
+                       </zipfileset>\r
+               </zip>\r
+       </target>\r
+                                       \r
        <!-- \r
-                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
-                       Build the Gitblit API client library\r
-                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
-               -->\r
-               <target name="buildApiLibrary" depends="compile" description="Builds the Gitblit RPC client library">\r
-                       <echo>Building Gitblit API Library ${gb.version}</echo>\r
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
+               Build the Gitblit API client library\r
+               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
+       -->\r
+       <target name="buildApiLibrary" depends="compile" description="Builds the Gitblit RPC client library">\r
+               <echo>Building Gitblit API Library ${gb.version}</echo>\r
+       \r
+               <!-- Build API Library jar -->\r
+               <genjar jarfile="gbapi-${gb.version}.jar">\r
+                       <class name="com.gitblit.Keys" />\r
+                       <class name="com.gitblit.client.GitblitClient" />\r
+                       <classpath refid="master-classpath" />\r
+                       <classfilter>\r
+                               <exclude name="com.google.gson." />\r
+                               <exclude name="com.sun.syndication." />\r
+                       </classfilter>\r
+                       <manifest>\r
+                               <attribute name="Specification-Version" value="${gb.version}" />\r
+                               <attribute name="Release-Date" value="${gb.versionDate}" />\r
+                       </manifest>\r
+               </genjar>\r
                \r
-                       <!-- Build API Library jar -->\r
-                       <genjar jarfile="gbapi-${gb.version}.jar">\r
-                               <class name="com.gitblit.Keys" />\r
-                               <class name="com.gitblit.client.GitblitClient" />\r
-                               <classpath refid="master-classpath" />\r
-                               <classfilter>\r
-                                       <exclude name="com.google.gson." />\r
-                                       <exclude name="com.sun.syndication." />\r
-                               </classfilter>\r
-                               <manifest>\r
-                                       <attribute name="Specification-Version" value="${gb.version}" />\r
-                                       <attribute name="Release-Date" value="${gb.versionDate}" />\r
-                               </manifest>\r
-                       </genjar>\r
-                       \r
-                       <!-- Build API sources jar -->\r
-                       <zip destfile="gbapi-${gb.version}-sources.jar">\r
-                               <fileset dir="${basedir}/src" defaultexcludes="yes">\r
-                                       <include name="com/gitblit/Constants.java"/>\r
-                                       <include name="com/gitblit/GitBlitException.java"/>\r
-                                       <include name="com/gitblit/Keys.java"/>\r
-                                       <include name="com/gitblit/client/**/*.java"/>\r
-                                       <include name="com/gitblit/models/**/*.java"/>\r
-                                       <include name="com/gitblit/utils/**/*.java"/>                                   \r
-                               </fileset>\r
-                       </zip>\r
-                       \r
-                       <!-- Build API JavaDoc jar -->\r
-                       <javadoc destdir="${basedir}/javadoc">\r
-                               <fileset dir="${basedir}/src" defaultexcludes="yes">\r
-                                       <include name="com/gitblit/Constants.java"/>\r
-                                       <include name="com/gitblit/GitBlitException.java"/>\r
-                                       <include name="com/gitblit/Keys.java"/>\r
-                                       <include name="com/gitblit/client/**/*.java"/>\r
-                                       <include name="com/gitblit/models/**/*.java"/>\r
-                                       <include name="com/gitblit/utils/**/*.java"/>                                   \r
-                               </fileset>\r
-                       </javadoc>\r
-                       <zip destfile="gbapi-${gb.version}-javadoc.jar">\r
-                               <fileset dir="${basedir}/javadoc" />\r
-                       </zip>\r
-                       \r
-                       <!-- Build the API library zip file -->\r
-                       <zip destfile="${gbapi.zipfile}">\r
-                               <fileset dir="${basedir}">\r
-                                       <include name="gbapi-${gb.version}.jar" />\r
-                                       <include name="gbapi-${gb.version}-sources.jar" />\r
-                                       <include name="gbapi-${gb.version}-javadoc.jar" />\r
-                                       <include name="LICENSE" />\r
-                                       <include name="NOTICE" />\r
-                               </fileset>\r
-                               <fileset dir="${basedir}/ext">\r
-                                       <exclude name="src/**" />\r
-                                       <include name="gson*.jar" />\r
-                                       <include name="rome*.jar" />\r
-                                       <include name="jdom*.jar" />\r
-                               </fileset>\r
-                       </zip>\r
-               </target>\r
+               <!-- Build API sources jar -->\r
+               <zip destfile="gbapi-${gb.version}-sources.jar">\r
+                       <fileset dir="${basedir}/src" defaultexcludes="yes">\r
+                               <include name="com/gitblit/Constants.java"/>\r
+                               <include name="com/gitblit/GitBlitException.java"/>\r
+                               <include name="com/gitblit/Keys.java"/>\r
+                               <include name="com/gitblit/client/**/*.java"/>\r
+                               <include name="com/gitblit/models/**/*.java"/>\r
+                               <include name="com/gitblit/utils/**/*.java"/>                                   \r
+                       </fileset>\r
+               </zip>\r
+               \r
+               <!-- Build API JavaDoc jar -->\r
+               <javadoc destdir="${basedir}/javadoc">\r
+                       <fileset dir="${basedir}/src" defaultexcludes="yes">\r
+                               <include name="com/gitblit/Constants.java"/>\r
+                               <include name="com/gitblit/GitBlitException.java"/>\r
+                               <include name="com/gitblit/Keys.java"/>\r
+                               <include name="com/gitblit/client/**/*.java"/>\r
+                               <include name="com/gitblit/models/**/*.java"/>\r
+                               <include name="com/gitblit/utils/**/*.java"/>                                   \r
+                       </fileset>\r
+               </javadoc>\r
+               <zip destfile="gbapi-${gb.version}-javadoc.jar">\r
+                       <fileset dir="${basedir}/javadoc" />\r
+               </zip>\r
+               \r
+               <!-- Build the API library zip file -->\r
+               <zip destfile="${gbapi.zipfile}">\r
+                       <fileset dir="${basedir}">\r
+                               <include name="gbapi-${gb.version}.jar" />\r
+                               <include name="gbapi-${gb.version}-sources.jar" />\r
+                               <include name="gbapi-${gb.version}-javadoc.jar" />\r
+                               <include name="LICENSE" />\r
+                               <include name="NOTICE" />\r
+                       </fileset>\r
+                       <fileset dir="${basedir}/ext">\r
+                               <exclude name="src/**" />\r
+                               <include name="gson*.jar" />\r
+                               <include name="rome*.jar" />\r
+                               <include name="jdom*.jar" />\r
+                       </fileset>\r
+               </zip>\r
+       </target>\r
                \r
                \r
        <!-- \r
                Compile from source, publish binaries, and build & deploy site\r
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r
        -->\r
-       <target name="buildAll" depends="buildGO,buildWAR,buildExpress,buildFederationClient,buildManager,buildApiLibrary,buildSite">           \r
+       <target name="buildAll" depends="buildAuthority,buildGO,buildWAR,buildExpress,buildFederationClient,buildManager,buildApiLibrary,buildSite">            \r
                <!-- Cleanup -->\r
                <delete dir="${project.build.dir}" />\r
                <delete dir="${project.war.dir}" />\r
                        summary="Gitblit Manager v${gb.version} (Swing tool to remotely administer a Gitblit server)"\r
                        labels="Featured, Type-Package, OpSys-All" />\r
 \r
+               <!-- Upload Gitblit Authority -->\r
+               <gcupload \r
+                       username="${googlecode.user}" \r
+                       password="${googlecode.password}" \r
+                       projectname="gitblit" \r
+                       filename="${authority.zipfile}" \r
+                       targetfilename="authority-${gb.version}.zip"\r
+                       summary="Gitblit Authority v${gb.version} (Swing tool to manage client SSL certificates)"\r
+                       labels="Featured, Type-Package, OpSys-All" />\r
+\r
                <!-- Upload Gitblit API Library -->\r
                <gcupload \r
                        username="${googlecode.user}" \r
index 99e92eeee986be1f588e214e82a27d08d2f07bf3..b83b714270b77a828d5a091285fd240b6026425d 100644 (file)
@@ -47,7 +47,7 @@ In order to fork a repository, the user account must have the *fork* permission
     **New:** *git.garbageCollectionHour = 0*  \r
     **New:** *git.defaultGarbageCollectionThreshold = 500k*  \r
     **New:** *git.defaultGarbageCollectionPeriod = 7 days*\r
-- Added support for X509 client certificate authentication (github/kevinanderson1).  \r
+- Added support for X509 client certificate authentication (github/kevinanderson1).  (issue 106)  \r
 You can require all git servlet access be authenticated by a client certificate.  You may also specify the OID fingerprint to use for mapping a certificate to a username.  It should be noted that the user account MUST already exist in Gitblit for this authentication mechanism to work; this mechanism can not be used to automatically create user accounts from a certificate.  \r
     **New:** *git.requireClientCertificates = false*  \r
     **New:** *git.enforceCertificateValidity = true*  \r
@@ -56,6 +56,7 @@ You can require all git servlet access be authenticated by a client certificate.
 - Added support for Gitblit GO to require usage of client certificates to access the entire server.  \r
 This is extreme and should be considered carefully since it affects every https access.  The default is to **want** client certificates.  Setting this value to *true* changes that to **need** client certificates.  \r
     **New:** *server.requireClientCertificates = false*\r
+- Added Gitblit Certificate Authority, an X509 certificate generation tool for Gitblit GO to encourage use of client certificate authentication.\r
 - Added setting to control length of shortened commit ids  \r
     **New:** *web.shortCommitIdLength=8*  \r
 - Added simple project pages.  A project is a subfolder off the *git.repositoriesFolder*.\r
diff --git a/resources/bullet_delete.png b/resources/bullet_delete.png
new file mode 100644 (file)
index 0000000..7736d69
Binary files /dev/null and b/resources/bullet_delete.png differ
diff --git a/resources/bullet_key.png b/resources/bullet_key.png
new file mode 100644 (file)
index 0000000..6260013
Binary files /dev/null and b/resources/bullet_key.png differ
diff --git a/resources/rosette_16x16.png b/resources/rosette_16x16.png
new file mode 100644 (file)
index 0000000..6dedc27
Binary files /dev/null and b/resources/rosette_16x16.png differ
diff --git a/resources/vcard_16x16.png b/resources/vcard_16x16.png
new file mode 100644 (file)
index 0000000..b8a9fea
Binary files /dev/null and b/resources/vcard_16x16.png differ
diff --git a/src/com/gitblit/authority/CertificateStatus.java b/src/com/gitblit/authority/CertificateStatus.java
new file mode 100644 (file)
index 0000000..79c5162
--- /dev/null
@@ -0,0 +1,20 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+public enum CertificateStatus {\r
+       unknown, ok, expiring, expired, revoked\r
+}\r
diff --git a/src/com/gitblit/authority/CertificateStatusRenderer.java b/src/com/gitblit/authority/CertificateStatusRenderer.java
new file mode 100644 (file)
index 0000000..7a708ea
--- /dev/null
@@ -0,0 +1,82 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.awt.Component;\r
+\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JTable;\r
+import javax.swing.table.DefaultTableCellRenderer;\r
+\r
+import com.gitblit.client.Translation;\r
+\r
+/**\r
+ * Displays a subscribed icon on the left of the repository name, if there is at\r
+ * least one subscribed branch.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class CertificateStatusRenderer extends DefaultTableCellRenderer {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       private final ImageIcon unknownIcon;\r
+       private final ImageIcon revokedIcon;\r
+       private final ImageIcon expiredIcon;\r
+       private final ImageIcon expiringIcon;\r
+       private final ImageIcon okIcon;\r
+\r
+       public CertificateStatusRenderer() {\r
+               super();\r
+               unknownIcon = new ImageIcon(getClass().getResource("/bullet_white.png"));\r
+               revokedIcon = new ImageIcon(getClass().getResource("/bullet_delete.png"));\r
+               expiredIcon = new ImageIcon(getClass().getResource("/bullet_red.png"));\r
+               expiringIcon = new ImageIcon(getClass().getResource("/bullet_orange.png"));\r
+               okIcon = new ImageIcon(getClass().getResource("/bullet_green.png"));\r
+       }\r
+\r
+       public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,\r
+                       boolean hasFocus, int row, int column) {\r
+               super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
+               if (value instanceof CertificateStatus) {\r
+                       CertificateStatus status = (CertificateStatus) value;\r
+                       switch(status) {\r
+                               case revoked:\r
+                                       setText(Translation.get("gb.revoked"));\r
+                                       setIcon(revokedIcon);\r
+                                       break;\r
+                               case expiring:\r
+                                       setText(Translation.get("gb.expiring"));\r
+                                       setIcon(expiringIcon);\r
+                                       break;\r
+                               case expired:\r
+                                       setText(Translation.get("gb.expired"));\r
+                                       setIcon(expiredIcon);\r
+                                       break;\r
+                               case unknown:\r
+                                       setText("");\r
+                                       setIcon(unknownIcon);\r
+                                       break;\r
+                               default:\r
+                                       setText(Translation.get("gb.ok"));\r
+                                       setIcon(okIcon);\r
+                                       break;\r
+                       }\r
+               }\r
+               return this;\r
+       }\r
+}\r
diff --git a/src/com/gitblit/authority/CertificatesTableModel.java b/src/com/gitblit/authority/CertificatesTableModel.java
new file mode 100644 (file)
index 0000000..44d80e3
--- /dev/null
@@ -0,0 +1,166 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.math.BigInteger;\r
+import java.security.cert.X509Certificate;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.Date;\r
+\r
+import javax.swing.table.AbstractTableModel;\r
+\r
+import com.gitblit.client.Translation;\r
+import com.gitblit.utils.X509Utils.RevocationReason;\r
+\r
+/**\r
+ * Table model of a list of user certificate models.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class CertificatesTableModel extends AbstractTableModel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       UserCertificateModel ucm;\r
+       \r
+       enum Columns {\r
+               SerialNumber, Status, Reason, Issued, Expires;\r
+\r
+               @Override\r
+               public String toString() {\r
+                       return name().replace('_', ' ');\r
+               }\r
+       }\r
+\r
+       public CertificatesTableModel() {\r
+       }\r
+\r
+       @Override\r
+       public int getRowCount() {\r
+               return ucm == null || ucm.certs == null ? 0 : ucm.certs.size();\r
+       }\r
+\r
+       @Override\r
+       public int getColumnCount() {\r
+               return Columns.values().length;\r
+       }\r
+\r
+       @Override\r
+       public String getColumnName(int column) {\r
+               Columns col = Columns.values()[column];\r
+               switch (col) {\r
+               case SerialNumber:\r
+                       return Translation.get("gb.serialNumber");\r
+               case Issued:\r
+                       return Translation.get("gb.issued");\r
+               case Expires:\r
+                       return Translation.get("gb.expires");\r
+               case Status:\r
+                       return Translation.get("gb.status");\r
+               case Reason:\r
+                       return Translation.get("gb.reason");\r
+               }\r
+               return "";\r
+       }\r
+\r
+       /**\r
+        * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.\r
+        * \r
+        * @param columnIndex\r
+        *            the column being queried\r
+        * @return the Object.class\r
+        */\r
+       public Class<?> getColumnClass(int columnIndex) {\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               case Status:\r
+                       return CertificateStatus.class;\r
+               case Issued:\r
+                       return Date.class;\r
+               case Expires:\r
+                       return Date.class;\r
+               case SerialNumber:\r
+                       return BigInteger.class;\r
+               default:\r
+                       return String.class;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public boolean isCellEditable(int rowIndex, int columnIndex) {\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               default:\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public Object getValueAt(int rowIndex, int columnIndex) {\r
+               X509Certificate cert = ucm.certs.get(rowIndex);\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               case Status:\r
+                       return ucm.getStatus(cert);\r
+               case SerialNumber:\r
+                       return cert.getSerialNumber();\r
+               case Issued:\r
+                       return cert.getNotBefore();\r
+               case Expires:\r
+                       return cert.getNotAfter();\r
+               case Reason:\r
+                       if (ucm.getStatus(cert).equals(CertificateStatus.revoked)) {\r
+                               RevocationReason r = ucm.getRevocationReason(cert.getSerialNumber());\r
+                               return Translation.get("gb." + r.name());\r
+                       }                       \r
+               }\r
+               return null;\r
+       }\r
+\r
+       public X509Certificate get(int modelRow) {\r
+               return ucm.certs.get(modelRow);\r
+       }\r
+       \r
+       public void setUserCertificateModel(UserCertificateModel ucm) {\r
+               this.ucm = ucm;\r
+               Collections.sort(ucm.certs, new Comparator<X509Certificate>() {\r
+                       @Override\r
+                       public int compare(X509Certificate o1, X509Certificate o2) {\r
+                               // sort by issue date in reverse chronological order\r
+                               int result = o2.getNotBefore().compareTo(o1.getNotBefore());\r
+                               if (result == 0) {\r
+                                       // same issue date, show expiring first\r
+                                       boolean r1 = CertificatesTableModel.this.ucm.isRevoked(o1.getSerialNumber());\r
+                                       boolean r2 = CertificatesTableModel.this.ucm.isRevoked(o2.getSerialNumber());\r
+                                       if ((r1 && r2) || (!r1 && !r2)) {\r
+                                               // both revoked or both not revoked\r
+                                               // chronlogical order by expiration dates\r
+                                               result = o1.getNotAfter().compareTo(o2.getNotAfter());\r
+                                       } else if (r1) {\r
+                                               // r1 is revoked, r2 first\r
+                                               return 1;\r
+                                       } else {\r
+                                               // r2 is revoked, r1 first\r
+                                               return -1;\r
+                                       }\r
+                               }\r
+                               return result;\r
+                       }\r
+               });\r
+       }\r
+}\r
index ff48ecf95e1e5236192d7d4be397d3c4f2253315..84162000d17abe912233866418bfa84bcfb6d7c8 100644 (file)
@@ -136,7 +136,7 @@ public class GitblitAuthority extends JFrame {
        \r
        public void initialize() {\r
                setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());\r
-               setTitle("Gitblit PKI Authority v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");\r
+               setTitle("Gitblit Certificate Authority v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");\r
                setContentPane(getUI());\r
                setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
                addWindowListener(new WindowAdapter() {\r
diff --git a/src/com/gitblit/authority/GitblitAuthorityLauncher.java b/src/com/gitblit/authority/GitblitAuthorityLauncher.java
new file mode 100644 (file)
index 0000000..584ac01
--- /dev/null
@@ -0,0 +1,113 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.awt.Color;\r
+import java.awt.EventQueue;\r
+import java.awt.FontMetrics;\r
+import java.awt.Graphics2D;\r
+import java.awt.SplashScreen;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import com.gitblit.Constants;\r
+import com.gitblit.Launcher;\r
+import com.gitblit.build.Build;\r
+import com.gitblit.build.Build.DownloadListener;\r
+import com.gitblit.client.Translation;\r
+\r
+/**\r
+ * Downloads dependencies and launches Gitblit Authority.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class GitblitAuthorityLauncher {\r
+\r
+       public static void main(String[] args) {\r
+               final SplashScreen splash = SplashScreen.getSplashScreen();\r
+               \r
+               DownloadListener downloadListener = new DownloadListener() {\r
+                       @Override\r
+                       public void downloading(String name) {\r
+                               updateSplash(splash, Translation.get("gb.downloading") + " " + name);                           \r
+                       }\r
+               };\r
+               \r
+               // download authority runtime dependencies\r
+               Build.authority(downloadListener);\r
+\r
+               File libFolder = new File("ext");\r
+               List<File> jars = Launcher.findJars(libFolder.getAbsoluteFile());\r
+               \r
+               // sort the jars by name and then reverse the order so the newer version\r
+               // of the library gets loaded in the event that this is an upgrade\r
+               Collections.sort(jars);\r
+               Collections.reverse(jars);\r
+               for (File jar : jars) {\r
+                       try {\r
+                               updateSplash(splash, Translation.get("gb.loading") + " " + jar.getName() + "...");\r
+                               Launcher.addJarFile(jar);\r
+                       } catch (IOException e) {\r
+\r
+                       }\r
+               }\r
+               \r
+               updateSplash(splash, Translation.get("gb.starting") + " Gitblit Authority...");\r
+               GitblitAuthority.main(args);\r
+       }\r
+\r
+       private static void updateSplash(final SplashScreen splash, final String string) {\r
+               if (splash == null) {\r
+                       return;\r
+               }\r
+               try {\r
+                       EventQueue.invokeAndWait(new Runnable() {\r
+                               public void run() {\r
+                                       Graphics2D g = splash.createGraphics();\r
+                                       if (g != null) {\r
+                                               // Splash is 320x120\r
+                                               FontMetrics fm = g.getFontMetrics();\r
+                                               \r
+                                               // paint startup status\r
+                                               g.setColor(Color.darkGray);\r
+                                               int h = fm.getHeight() + fm.getMaxDescent();\r
+                                               int x = 5;\r
+                                               int y = 115;\r
+                                               int w = 320 - 2 * x;\r
+                                               g.fillRect(x, y - h, w, h);\r
+                                               g.setColor(Color.lightGray);\r
+                                               g.drawRect(x, y - h, w, h);\r
+                                               g.setColor(Color.WHITE);\r
+                                               int xw = fm.stringWidth(string);\r
+                                               g.drawString(string, x + ((w - xw) / 2), y - 5);\r
+                                               \r
+                                               // paint version\r
+                                               String ver = "v" + Constants.VERSION;\r
+                                               int vw = g.getFontMetrics().stringWidth(ver);\r
+                                               g.drawString(ver, 320 - vw - 5, 34);\r
+                                               g.dispose();\r
+                                               splash.update();\r
+                                       }\r
+                               }\r
+                       });\r
+               } catch (Throwable t) {\r
+                       t.printStackTrace();\r
+               }\r
+       }\r
+}\r
diff --git a/src/com/gitblit/authority/NewClientCertificateDialog.java b/src/com/gitblit/authority/NewClientCertificateDialog.java
new file mode 100644 (file)
index 0000000..ad4fe9a
--- /dev/null
@@ -0,0 +1,164 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Frame;\r
+import java.awt.GridLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.util.Date;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JPasswordField;\r
+import javax.swing.JTextField;\r
+\r
+import org.bouncycastle.util.Arrays;\r
+\r
+import com.gitblit.client.HeaderPanel;\r
+import com.gitblit.client.Translation;\r
+import com.gitblit.utils.StringUtils;\r
+import com.toedter.calendar.JDateChooser;\r
+\r
+public class NewClientCertificateDialog extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       JDateChooser expirationDate;\r
+       JPasswordField pw1;\r
+       JPasswordField pw2;\r
+       JTextField hint;\r
+       JCheckBox sendEmail;\r
+       boolean isCanceled = true;\r
+\r
+       public NewClientCertificateDialog(Frame owner, String displayname, Date defaultExpiration) {\r
+               super(owner);\r
+               \r
+               setTitle(Translation.get("gb.newCertificate"));\r
+               \r
+               JPanel content = new JPanel(new BorderLayout(5, 5)) {                   \r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Insets getInsets() {\r
+                               \r
+                               return Utils.INSETS;\r
+                       }\r
+               };\r
+               content.add(new HeaderPanel(Translation.get("gb.newCertificate") + " (" + displayname + ")", "rosette_16x16.png"), BorderLayout.NORTH);\r
+               \r
+               expirationDate = new JDateChooser(defaultExpiration);\r
+               pw1 = new JPasswordField(20);\r
+               pw2 = new JPasswordField(20);\r
+               hint = new JTextField(20);\r
+               sendEmail = new JCheckBox(Translation.get("gb.sendEmail"));\r
+               \r
+               JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));\r
+               \r
+               panel.add(new JLabel(Translation.get("gb.expires")));\r
+               panel.add(expirationDate);\r
+               \r
+               panel.add(new JLabel(Translation.get("gb.password")));\r
+               panel.add(pw1);\r
+\r
+               panel.add(new JLabel(Translation.get("gb.confirmPassword")));\r
+               panel.add(pw2);\r
+               \r
+               panel.add(new JLabel(Translation.get("gb.passwordHint")));\r
+               panel.add(hint);\r
+               \r
+               panel.add(new JLabel(""));\r
+               panel.add(sendEmail);\r
+\r
+               content.add(panel, BorderLayout.CENTER);\r
+               \r
+               JButton ok = new JButton(Translation.get("gb.ok"));\r
+               ok.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               if (validateInputs()) {\r
+                                       isCanceled = false;\r
+                                       setVisible(false);\r
+                               }\r
+                       }\r
+               });\r
+               JButton cancel = new JButton(Translation.get("gb.cancel"));\r
+               cancel.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               isCanceled = true;\r
+                               setVisible(false);\r
+                       }\r
+               });\r
+               \r
+               JPanel controls = new JPanel();\r
+               controls.add(ok);\r
+               controls.add(cancel);\r
+               \r
+               content.add(controls, BorderLayout.SOUTH);\r
+               \r
+               getContentPane().add(content, BorderLayout.CENTER);\r
+               pack();\r
+               \r
+               setLocationRelativeTo(owner);\r
+       }\r
+       \r
+       private boolean validateInputs() {\r
+               if (getExpiration().getTime() < System.currentTimeMillis()) {\r
+                       // expires before now\r
+                       JOptionPane.showMessageDialog(this, Translation.get("gb.invalidExpiraitonDate"),\r
+                                       Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);\r
+                       return false;\r
+               }\r
+               if (pw1.getPassword().length == 0 || !Arrays.areEqual(pw1.getPassword(), pw2.getPassword())) {\r
+                       // password mismatch\r
+                       JOptionPane.showMessageDialog(this, Translation.get("gb.passwordsDoNotMatch"),\r
+                                       Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);\r
+                       return false;\r
+               }\r
+               if (StringUtils.isEmpty(getPasswordHint())) {\r
+                       // must have hint\r
+                       JOptionPane.showMessageDialog(this, Translation.get("gb.passwordHintRequired"),\r
+                                       Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);\r
+                       return false;\r
+               }\r
+               return true;\r
+       }\r
+       \r
+       public String getPassword() {\r
+               return new String(pw1.getPassword());\r
+       }\r
+       \r
+       public String getPasswordHint() {\r
+               return hint.getText();\r
+       }\r
+       \r
+       public Date getExpiration() {\r
+               return expirationDate.getDate();\r
+       }\r
+       \r
+       public boolean sendEmail() {\r
+               return sendEmail.isSelected();\r
+       }\r
+       \r
+       public boolean isCanceled() {\r
+               return isCanceled;\r
+       }\r
+}\r
diff --git a/src/com/gitblit/authority/UserCertificatePanel.java b/src/com/gitblit/authority/UserCertificatePanel.java
new file mode 100644 (file)
index 0000000..20727f6
--- /dev/null
@@ -0,0 +1,334 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Component;\r
+import java.awt.Cursor;\r
+import java.awt.Dimension;\r
+import java.awt.FlowLayout;\r
+import java.awt.Frame;\r
+import java.awt.GridLayout;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.MouseAdapter;\r
+import java.awt.event.MouseEvent;\r
+import java.security.cert.X509Certificate;\r
+import java.util.Date;\r
+\r
+import javax.swing.ImageIcon;\r
+import javax.swing.JButton;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextField;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+import javax.swing.table.TableRowSorter;\r
+\r
+import com.gitblit.client.HeaderPanel;\r
+import com.gitblit.client.Translation;\r
+import com.gitblit.models.UserModel;\r
+import com.gitblit.utils.X509Utils.RevocationReason;\r
+import com.gitblit.utils.X509Utils.X509Metadata;\r
+\r
+public abstract class UserCertificatePanel extends JPanel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       private Frame owner;\r
+       \r
+       private UserCertificateModel ucm;\r
+       \r
+       private JTextField displayname;\r
+       private JTextField username;\r
+       private JTextField emailAddress;\r
+       private JTextField organizationalUnit;\r
+       private JTextField organization;\r
+       private JTextField locality;\r
+       private JTextField stateProvince;\r
+       private JTextField countryCode;\r
+\r
+       private CertificatesTableModel tableModel;\r
+\r
+       private JButton saveUserButton;\r
+\r
+       private JButton editUserButton;\r
+\r
+       private JButton newCertificateButton;\r
+       \r
+       private JButton revokeCertificateButton;\r
+\r
+       private JTable table;\r
+       \r
+       public UserCertificatePanel(Frame owner) {\r
+               super(new BorderLayout());\r
+               \r
+               this.owner = owner;\r
+               \r
+               displayname = new JTextField(20);\r
+               username = new JTextField(20);\r
+               username.setEditable(false);\r
+               emailAddress = new JTextField(20);\r
+               organizationalUnit = new JTextField(20);\r
+               organization = new JTextField(20);\r
+               locality = new JTextField(20);\r
+               stateProvince = new JTextField(20);\r
+               countryCode = new JTextField(20);\r
+                               \r
+               JPanel fields = new JPanel(new GridLayout(0, 1, 5, 5));\r
+               fields.add(newFieldPanel(Translation.get("gb.displayName"), displayname));\r
+               fields.add(newFieldPanel(Translation.get("gb.username") + " (CN)", username));\r
+               fields.add(newFieldPanel(Translation.get("gb.emailAddress") + " (E)", emailAddress));\r
+               fields.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit));\r
+               fields.add(newFieldPanel(Translation.get("gb.organization") + " (O)", organization));\r
+               fields.add(newFieldPanel(Translation.get("gb.locality") + " (L)", locality));\r
+               fields.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince));\r
+               fields.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode));\r
+               \r
+               JPanel fp = new JPanel(new BorderLayout(5, 5));\r
+               fp.add(fields, BorderLayout.NORTH);\r
+               \r
+               JPanel fieldsPanel = new JPanel(new BorderLayout());\r
+               fieldsPanel.add(new HeaderPanel(Translation.get("gb.properties"), "vcard_16x16.png"), BorderLayout.NORTH);\r
+               fieldsPanel.add(fp, BorderLayout.CENTER);\r
+               \r
+               saveUserButton = new JButton(Translation.get("gb.save"));\r
+               saveUserButton.setEnabled(false);\r
+               saveUserButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               setEditable(false);\r
+                               String username = ucm.user.username;\r
+                               updateUser();\r
+                               saveUser(username, ucm);\r
+                       }\r
+               });\r
+               \r
+               editUserButton = new JButton(Translation.get("gb.edit"));\r
+               editUserButton.setEnabled(false);\r
+               editUserButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               setEditable(true);\r
+                       }\r
+               });\r
+               \r
+               JPanel userControls = new JPanel(new FlowLayout(FlowLayout.LEFT));\r
+               userControls.add(editUserButton);\r
+               userControls.add(saveUserButton);\r
+               fieldsPanel.add(userControls, BorderLayout.SOUTH);\r
+               \r
+               JPanel certificatesPanel = new JPanel(new BorderLayout());\r
+               certificatesPanel.add(new HeaderPanel(Translation.get("gb.certificates"), "rosette_16x16.png"), BorderLayout.NORTH);            \r
+               tableModel = new CertificatesTableModel();\r
+               table = Utils.newTable(tableModel, Utils.DATE_FORMAT);\r
+               table.setRowSorter(new TableRowSorter<CertificatesTableModel>(tableModel));\r
+               table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer());\r
+               table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {\r
+                       @Override\r
+                       public void valueChanged(ListSelectionEvent e) {\r
+                               if (e.getValueIsAdjusting()) {\r
+                                       return;\r
+                               }\r
+                               boolean enable = false;\r
+                               int row = table.getSelectedRow();\r
+                               if (row > -1) {\r
+                                       int modelIndex = table.convertRowIndexToModel(row);\r
+                                       X509Certificate cert = tableModel.get(modelIndex);\r
+                                       enable = !ucm.isRevoked(cert.getSerialNumber());\r
+                               }\r
+                               revokeCertificateButton.setEnabled(enable);\r
+                       }\r
+               });\r
+               table.addMouseListener(new MouseAdapter() {\r
+                       public void mouseClicked(MouseEvent e) {\r
+                               if (e.getClickCount() == 2) {                                   \r
+                                       int row = table.rowAtPoint(e.getPoint());\r
+                                       int modelIndex = table.convertRowIndexToModel(row);\r
+                                       X509Certificate cert = tableModel.get(modelIndex);\r
+                                       X509CertificateViewer viewer = new X509CertificateViewer(UserCertificatePanel.this.owner, cert);                                        \r
+                                       viewer.setVisible(true);\r
+                               }\r
+                       }\r
+               });\r
+               certificatesPanel.add(new JScrollPane(table), BorderLayout.CENTER);\r
+               \r
+               newCertificateButton = new JButton(Translation.get("gb.newCertificate"));\r
+               newCertificateButton.setEnabled(false);\r
+               newCertificateButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               try {\r
+                                       if (saveUserButton.isEnabled()) {\r
+                                               // save changes\r
+                                               String username = ucm.user.username;\r
+                                               setEditable(false);\r
+                                               updateUser();\r
+                                               saveUser(username, ucm);\r
+                                       }\r
+                                       \r
+                                       NewClientCertificateDialog dialog = new NewClientCertificateDialog(UserCertificatePanel.this.owner,\r
+                                                       ucm.user.getDisplayName(), getDefaultExpiration());\r
+                                       dialog.setModal(true);\r
+                                       dialog.setVisible(true);\r
+                                       if (dialog.isCanceled()) {\r
+                                               return;\r
+                                       }\r
+                                       \r
+                                       setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+                                       UserModel user = ucm.user;\r
+                                       X509Metadata metadata = new X509Metadata(user.username, dialog.getPassword());\r
+                                       metadata.userDisplayname = user.getDisplayName();\r
+                                       metadata.emailAddress = user.emailAddress;                              \r
+                                       metadata.passwordHint = dialog.getPasswordHint();\r
+                                       metadata.notAfter = dialog.getExpiration();\r
+\r
+                                       newCertificate(ucm, metadata, dialog.sendEmail());\r
+                               } catch (Exception x) {\r
+                                       Utils.showException(UserCertificatePanel.this, x);\r
+                               } finally {\r
+                                       setCursor(Cursor.getDefaultCursor());\r
+                               }\r
+                       }\r
+               });\r
+               \r
+               revokeCertificateButton = new JButton(Translation.get("gb.revokeCertificate"));\r
+               revokeCertificateButton.setEnabled(false);\r
+               revokeCertificateButton.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               try {\r
+                                       int row = table.getSelectedRow();\r
+                                       if (row < 0) {\r
+                                               return;\r
+                                       }\r
+                                       int modelIndex = table.convertRowIndexToModel(row);\r
+                                       X509Certificate cert = tableModel.get(modelIndex);\r
+                                       \r
+                                       String [] choices = new String[RevocationReason.reasons.length];\r
+                                       for (int i = 0; i < choices.length; i++) {\r
+                                               choices[i] = Translation.get("gb." + RevocationReason.reasons[i].name());\r
+                                       }\r
+                                       \r
+                                       Object choice = JOptionPane.showInputDialog(UserCertificatePanel.this.owner,\r
+                                                       Translation.get("gb.revokeCertificateReason"), Translation.get("gb.revokeCertificate"),\r
+                                                       JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("/rosette_16x16.png")), choices, Translation.get("gb.unspecified"));\r
+                                       if (choice == null) {\r
+                                               return;\r
+                                       }\r
+                                       RevocationReason reason = RevocationReason.unspecified;\r
+                                       for (int i = 0 ; i < choices.length; i++) {\r
+                                               if (choices[i].equals(choice)) {\r
+                                                       reason = RevocationReason.reasons[i];\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                                       if (!ucm.isRevoked(cert.getSerialNumber())) {\r
+                                               if (ucm.certs.size() == 1) {\r
+                                                       // no other certificates\r
+                                                       ucm.expires = null;\r
+                                               } else {\r
+                                                       // determine new expires date for user\r
+                                                       Date newExpires = null;\r
+                                                       for (X509Certificate c : ucm.certs) {                                                           \r
+                                                               if (!c.equals(cert)) {\r
+                                                                       if (!ucm.isRevoked(c.getSerialNumber())) {\r
+                                                                               if (newExpires == null || c.getNotAfter().after(newExpires)) {\r
+                                                                                       newExpires = c.getNotAfter();\r
+                                                                               }\r
+                                                                       }\r
+                                                               }\r
+                                                       }\r
+                                                       ucm.expires = newExpires;\r
+                                               }\r
+                                               revoke(ucm, cert, reason);\r
+                                       }\r
+                               } catch (Exception x) {\r
+                                       Utils.showException(UserCertificatePanel.this, x);\r
+                               } finally {\r
+                                       setCursor(Cursor.getDefaultCursor());\r
+                               }\r
+                       }\r
+               });\r
+               \r
+               JPanel certificateControls = new JPanel(new FlowLayout(FlowLayout.LEFT));\r
+               certificateControls.add(newCertificateButton);\r
+               certificateControls.add(revokeCertificateButton);\r
+               certificatesPanel.add(certificateControls, BorderLayout.SOUTH);\r
+                               \r
+               add(fieldsPanel, BorderLayout.NORTH);\r
+               add(certificatesPanel, BorderLayout.CENTER);\r
+               setEditable(false);\r
+       }\r
+       \r
+       private JPanel newFieldPanel(String label, Component c) {\r
+               JLabel jlabel = new JLabel(label);\r
+               jlabel.setPreferredSize(new Dimension(150, 20));\r
+               JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));\r
+               panel.add(jlabel);\r
+               panel.add(c);\r
+               return panel;\r
+       }\r
+       \r
+       public void setUserCertificateModel(UserCertificateModel ucm) {\r
+               this.ucm = ucm;\r
+               setEditable(false);\r
+               displayname.setText(ucm.user.getDisplayName());\r
+               username.setText(ucm.user.username);\r
+               emailAddress.setText(ucm.user.emailAddress);\r
+               organizationalUnit.setText(ucm.user.organizationalUnit);\r
+               organization.setText(ucm.user.organization);\r
+               locality.setText(ucm.user.locality);\r
+               stateProvince.setText(ucm.user.stateProvince);\r
+               countryCode.setText(ucm.user.countryCode);\r
+               \r
+               tableModel.setUserCertificateModel(ucm);\r
+               tableModel.fireTableDataChanged();\r
+       }\r
+       \r
+       public void setEditable(boolean editable) {\r
+               displayname.setEditable(editable);\r
+//             username.setEditable(editable);\r
+               emailAddress.setEditable(editable);\r
+               organizationalUnit.setEditable(editable);\r
+               organization.setEditable(editable);\r
+               locality.setEditable(editable);\r
+               stateProvince.setEditable(editable);\r
+               countryCode.setEditable(editable);\r
+               \r
+               editUserButton.setEnabled(!editable && ucm != null);\r
+               saveUserButton.setEnabled(editable && ucm != null);\r
+               \r
+               newCertificateButton.setEnabled(ucm != null);\r
+               revokeCertificateButton.setEnabled(false);\r
+       }\r
+       \r
+       private void updateUser() {\r
+               ucm.user.displayName = displayname.getText();\r
+               ucm.user.username = username.getText();\r
+               ucm.user.emailAddress = emailAddress.getText();\r
+               ucm.user.organizationalUnit = organizationalUnit.getText();\r
+               ucm.user.organization = organization.getText();\r
+               ucm.user.locality = locality.getText();\r
+               ucm.user.stateProvince = stateProvince.getText();\r
+               ucm.user.countryCode = countryCode.getText();\r
+       }\r
+       \r
+       public abstract Date getDefaultExpiration();\r
+       \r
+       public abstract void saveUser(String username, UserCertificateModel ucm);\r
+       public abstract void newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail);\r
+       public abstract void revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason);\r
+}\r
diff --git a/src/com/gitblit/authority/UserCertificateTableModel.java b/src/com/gitblit/authority/UserCertificateTableModel.java
new file mode 100644 (file)
index 0000000..dde73fc
--- /dev/null
@@ -0,0 +1,131 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.List;\r
+\r
+import javax.swing.table.AbstractTableModel;\r
+\r
+import com.gitblit.client.Translation;\r
+\r
+/**\r
+ * Table model of a list of user certificate models.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class UserCertificateTableModel extends AbstractTableModel {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+\r
+       List<UserCertificateModel> list;\r
+\r
+       enum Columns {\r
+               Username, DisplayName, Status, Expires;\r
+\r
+               @Override\r
+               public String toString() {\r
+                       return name().replace('_', ' ');\r
+               }\r
+       }\r
+\r
+       public UserCertificateTableModel() {\r
+               this(new ArrayList<UserCertificateModel>());\r
+       }\r
+\r
+       public UserCertificateTableModel(List<UserCertificateModel> list) {\r
+               this.list = list;\r
+               Collections.sort(this.list);\r
+       }\r
+\r
+       @Override\r
+       public int getRowCount() {\r
+               return list.size();\r
+       }\r
+\r
+       @Override\r
+       public int getColumnCount() {\r
+               return Columns.values().length;\r
+       }\r
+\r
+       @Override\r
+       public String getColumnName(int column) {\r
+               Columns col = Columns.values()[column];\r
+               switch (col) {\r
+               case Username:\r
+                       return Translation.get("gb.username");\r
+               case DisplayName:\r
+                       return Translation.get("gb.displayName");\r
+               case Expires:\r
+                       return Translation.get("gb.expires");\r
+               case Status:\r
+                       return Translation.get("gb.status");\r
+               }\r
+               return "";\r
+       }\r
+\r
+       /**\r
+        * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.\r
+        * \r
+        * @param columnIndex\r
+        *            the column being queried\r
+        * @return the Object.class\r
+        */\r
+       public Class<?> getColumnClass(int columnIndex) {\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               case Expires:\r
+                       return Date.class;\r
+               case Status:\r
+                       return CertificateStatus.class;\r
+               default:\r
+                       return String.class;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public boolean isCellEditable(int rowIndex, int columnIndex) {\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               default:\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       @Override\r
+       public Object getValueAt(int rowIndex, int columnIndex) {\r
+               UserCertificateModel model = list.get(rowIndex);\r
+               Columns col = Columns.values()[columnIndex];\r
+               switch (col) {\r
+               case Username:\r
+                       return model.user.username;\r
+               case DisplayName:\r
+                       return model.user.getDisplayName();\r
+               case Expires:\r
+                       return model.expires;\r
+               case Status:\r
+                       return model.getStatus();\r
+               }\r
+               return null;\r
+       }\r
+\r
+       public UserCertificateModel get(int modelRow) {\r
+               return list.get(modelRow);\r
+       }\r
+}\r
diff --git a/src/com/gitblit/authority/Utils.java b/src/com/gitblit/authority/Utils.java
new file mode 100644 (file)
index 0000000..5c82493
--- /dev/null
@@ -0,0 +1,101 @@
+package com.gitblit.authority;\r
+\r
+import java.awt.Color;\r
+import java.awt.Component;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Insets;\r
+import java.io.PrintWriter;\r
+import java.io.StringWriter;\r
+import java.util.Date;\r
+\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTable;\r
+import javax.swing.JTextArea;\r
+import javax.swing.table.DefaultTableColumnModel;\r
+import javax.swing.table.TableCellRenderer;\r
+import javax.swing.table.TableColumn;\r
+import javax.swing.table.TableModel;\r
+\r
+import com.gitblit.client.DateCellRenderer;\r
+import com.gitblit.client.Translation;\r
+\r
+public class Utils {\r
+\r
+       public final static int MARGIN = 5;\r
+\r
+       public final static Insets INSETS = new Insets(MARGIN, MARGIN, MARGIN, MARGIN);\r
+\r
+       public final static String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm";\r
+\r
+       public final static String DATE_FORMAT = "yyyy-MM-dd";\r
+\r
+       public static JTable newTable(TableModel model, String datePattern) {\r
+               JTable table = new JTable(model);\r
+               table.setRowHeight(table.getFont().getSize() + 8);\r
+               table.setCellSelectionEnabled(false);\r
+               table.setRowSelectionAllowed(true);\r
+               table.getTableHeader().setReorderingAllowed(false);\r
+               table.setGridColor(new Color(0xd9d9d9));\r
+               table.setBackground(Color.white);\r
+               table.setDefaultRenderer(Date.class,\r
+                               new DateCellRenderer(datePattern, Color.orange.darker()));\r
+               return table;\r
+       }\r
+       \r
+       public static void showException(Component c, Throwable t) {\r
+               StringWriter writer = new StringWriter();\r
+               t.printStackTrace(new PrintWriter(writer));\r
+               String stacktrace = writer.toString();\r
+               try {\r
+                       writer.close();\r
+               } catch (Throwable x) {\r
+               }\r
+               JTextArea textArea = new JTextArea(stacktrace);\r
+               textArea.setFont(new Font("monospaced", Font.PLAIN, 11));\r
+               JScrollPane jsp = new JScrollPane(textArea);\r
+               jsp.setPreferredSize(new Dimension(800, 400));\r
+               JOptionPane.showMessageDialog(c, jsp, Translation.get("gb.error"),\r
+                               JOptionPane.ERROR_MESSAGE);\r
+       }\r
+       \r
+       public static void packColumns(JTable table, int margin) {\r
+               for (int c = 0; c < table.getColumnCount(); c++) {\r
+                       packColumn(table, c, 4);\r
+               }\r
+       }\r
+\r
+       // Sets the preferred width of the visible column specified by vColIndex.\r
+       // The column will be just wide enough to show the column head and the\r
+       // widest cell in the column. margin pixels are added to the left and right\r
+       // (resulting in an additional width of 2*margin pixels).\r
+       private static void packColumn(JTable table, int vColIndex, int margin) {\r
+               DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel();\r
+               TableColumn col = colModel.getColumn(vColIndex);\r
+               int width = 0;\r
+\r
+               // Get width of column header\r
+               TableCellRenderer renderer = col.getHeaderRenderer();\r
+               if (renderer == null) {\r
+                       renderer = table.getTableHeader().getDefaultRenderer();\r
+               }\r
+               Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false,\r
+                               false, 0, 0);\r
+               width = comp.getPreferredSize().width;\r
+\r
+               // Get maximum width of column data\r
+               for (int r = 0; r < table.getRowCount(); r++) {\r
+                       renderer = table.getCellRenderer(r, vColIndex);\r
+                       comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex),\r
+                                       false, false, r, vColIndex);\r
+                       width = Math.max(width, comp.getPreferredSize().width);\r
+               }\r
+\r
+               // Add margin\r
+               width += 2 * margin;\r
+\r
+               // Set the width\r
+               col.setPreferredWidth(width);\r
+       }\r
+}\r
diff --git a/src/com/gitblit/authority/X509CertificateViewer.java b/src/com/gitblit/authority/X509CertificateViewer.java
new file mode 100644 (file)
index 0000000..d29c6e7
--- /dev/null
@@ -0,0 +1,129 @@
+/*\r
+ * Copyright 2012 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.authority;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Dimension;\r
+import java.awt.FlowLayout;\r
+import java.awt.Frame;\r
+import java.awt.GridLayout;\r
+import java.awt.Insets;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.security.cert.CertificateEncodingException;\r
+import java.security.cert.X509Certificate;\r
+import java.text.DateFormat;\r
+\r
+import javax.swing.JButton;\r
+import javax.swing.JDialog;\r
+import javax.swing.JLabel;\r
+import javax.swing.JPanel;\r
+import javax.swing.JTextField;\r
+import javax.swing.SwingConstants;\r
+\r
+import com.gitblit.client.HeaderPanel;\r
+import com.gitblit.client.Translation;\r
+import com.gitblit.utils.StringUtils;\r
+\r
+public class X509CertificateViewer extends JDialog {\r
+\r
+       private static final long serialVersionUID = 1L;\r
+       \r
+       public X509CertificateViewer(Frame owner, X509Certificate cert) {\r
+               super(owner);\r
+               \r
+               setTitle(Translation.get("gb.viewCertificate"));\r
+               \r
+               JPanel content = new JPanel(new BorderLayout(5, 5)) {                   \r
+                       private static final long serialVersionUID = 1L;\r
+\r
+                       @Override\r
+                       public Insets getInsets() {\r
+                               \r
+                               return Utils.INSETS;\r
+                       }\r
+               };\r
+               content.add(new HeaderPanel("certificiate", "rosette_16x16.png"), BorderLayout.NORTH);\r
+               \r
+               DateFormat df = DateFormat.getDateTimeInstance();\r
+               \r
+               int l1 = 15;\r
+               int l2 = 25;\r
+               int l3 = 45;\r
+               JPanel panel = new JPanel(new GridLayout(0, 1, 0, 10));\r
+               panel.add(newField(Translation.get("gb.version"), "" + cert.getVersion(), 3));\r
+               panel.add(newField(Translation.get("gb.subject"), cert.getSubjectDN().getName(), l3));\r
+               panel.add(newField(Translation.get("gb.issuer"), cert.getIssuerDN().getName(), l3));\r
+               panel.add(newField(Translation.get("gb.serialNumber"), "0x" + cert.getSerialNumber().toString(16), l2));\r
+               panel.add(newField(Translation.get("gb.serialNumber"), cert.getSerialNumber().toString(), l2));\r
+               panel.add(newField(Translation.get("gb.validFrom"), df.format(cert.getNotBefore()), l2));\r
+               panel.add(newField(Translation.get("gb.validUntil"), df.format(cert.getNotAfter()), l2));\r
+               panel.add(newField(Translation.get("gb.publicKey"), cert.getPublicKey().getAlgorithm(), l1));\r
+               panel.add(newField(Translation.get("gb.signatureAlgorithm"), cert.getSigAlgName(), l1));\r
+               try {\r
+                       panel.add(newField(Translation.get("gb.sha1FingerPrint"), fingerprint(StringUtils.getSHA1(cert.getEncoded())), l3));\r
+               } catch (CertificateEncodingException e1) {\r
+               }\r
+               try {\r
+                       panel.add(newField(Translation.get("gb.md5FingerPrint"), fingerprint(StringUtils.getMD5(cert.getEncoded())), l3));\r
+               } catch (CertificateEncodingException e1) {\r
+               }\r
+\r
+               content.add(panel, BorderLayout.CENTER);\r
+               \r
+               JButton ok = new JButton(Translation.get("gb.ok"));\r
+               ok.addActionListener(new ActionListener() {\r
+                       public void actionPerformed(ActionEvent e) {\r
+                               setVisible(false);\r
+                       }\r
+               });\r
+               \r
+               JPanel controls = new JPanel();\r
+               controls.add(ok);\r
+               \r
+               content.add(controls, BorderLayout.SOUTH);\r
+               \r
+               getContentPane().add(content, BorderLayout.CENTER);\r
+               pack();\r
+               \r
+               setLocationRelativeTo(owner);\r
+       }\r
+       \r
+       private JPanel newField(String label, String value, int cols) {\r
+               JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));\r
+               JLabel lbl = new JLabel(label);\r
+               lbl.setHorizontalAlignment(SwingConstants.RIGHT);\r
+               lbl.setPreferredSize(new Dimension(125, 20));\r
+               panel.add(lbl);\r
+               JTextField tf = new JTextField(value, cols);\r
+               tf.setCaretPosition(0);\r
+               tf.setEditable(false);\r
+               panel.add(tf);\r
+               return panel;\r
+       }\r
+       \r
+       private String fingerprint(String value) {\r
+               value = value.toUpperCase();\r
+               StringBuilder sb = new StringBuilder();\r
+               for (int i = 0; i < value.length(); i += 2) {\r
+                       sb.append(value.charAt(i));\r
+                       sb.append(value.charAt(i + 1));\r
+                       sb.append(':');\r
+               }\r
+               sb.setLength(sb.length() - 1);\r
+               return sb.toString();\r
+       }\r
+}\r
index 954dad2a26a8678b9ca246e84232f35af5c77966..751c7dbb8c4f679431e8936b0acf55be328a1341 100644 (file)
@@ -24,8 +24,6 @@ import javax.swing.JTable;
 import javax.swing.SwingConstants;\r
 import javax.swing.table.DefaultTableCellRenderer;\r
 \r
-import com.gitblit.utils.TimeUtils;\r
-\r
 /**\r
  * Time ago cell renderer with real date tooltip.\r
  * \r
@@ -55,7 +53,13 @@ public class DateCellRenderer extends DefaultTableCellRenderer {
                                title = "--";\r
                                dateString = "never";\r
                        } else {\r
-                               title = Translation.getTimeUtils().timeAgo(date);\r
+                               if (date.getTime() - System.currentTimeMillis() > 0) {\r
+                                       // future\r
+                                       title = Translation.getTimeUtils().inFuture(date);\r
+                               } else {\r
+                                       // past\r
+                                       title = Translation.getTimeUtils().timeAgo(date);\r
+                               }\r
                                dateString = new SimpleDateFormat(pattern).format((Date) value);\r
                        }\r
 \r
index 325b529f41aefd6795f085c4b5171906838d6cf2..868400486a6aade2db33fa1f88f9ff5c2f39fb45 100644 (file)
@@ -262,14 +262,26 @@ public class StringUtils {
         * @return md5 of the string\r
         */\r
        public static String getMD5(String string) {\r
+               try {\r
+                       return getMD5(string.getBytes("iso-8859-1"));\r
+               } catch (UnsupportedEncodingException u) {\r
+                       throw new RuntimeException(u);\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Calculates the MD5 of the string.\r
+        * \r
+        * @param string\r
+        * @return md5 of the string\r
+        */\r
+       public static String getMD5(byte [] bytes) {\r
                try {\r
                        MessageDigest md = MessageDigest.getInstance("MD5");\r
                        md.reset();\r
-                       md.update(string.getBytes("iso-8859-1"));\r
+                       md.update(bytes);\r
                        byte[] digest = md.digest();\r
                        return toHex(digest);\r
-               } catch (UnsupportedEncodingException u) {\r
-                       throw new RuntimeException(u);\r
                } catch (NoSuchAlgorithmException t) {\r
                        throw new RuntimeException(t);\r
                }\r
index 7f695625a41a410e81c92f3442100eac02ba38dd..ec8871c6ec84f6f5f260ac500e7fa8eb754ea9e5 100644 (file)
@@ -268,6 +268,22 @@ public class TimeUtils {
                }\r
        }\r
        \r
+       public String inFuture(Date date) {\r
+               long diff = date.getTime() - System.currentTimeMillis();\r
+               if (diff > ONEDAY) {\r
+                       double days = ((double) diff)/ONEDAY;\r
+                       return translate((int) Math.round(days), "gb.time.inDays", "in {0} days");\r
+               } else {\r
+                       double hours = ((double) diff)/ONEHOUR;\r
+                       if (hours > 2) {\r
+                               return translate((int) Math.round(hours), "gb.time.inHours", "in {0} hours");\r
+                       } else {\r
+                               int mins = (int) (diff/MIN);\r
+                               return translate(mins, "gb.time.inMinutes", "in {0} minutes");\r
+                       }\r
+               }\r
+       }\r
+       \r
        private String translate(String key, String defaultValue) {\r
                String value = defaultValue;\r
                if (translation != null && translation.containsKey(key)) {\r
index 22ae92f7b436c0e1d8ab760a0c52e4789ebeace3..6b2102ef002b4cb5fc8bb0a22aab35e83a929a57 100644 (file)
@@ -374,3 +374,44 @@ gb.missingPermission = the repository for this permission is missing!
 gb.mutable = mutable\r
 gb.specified = specified\r
 gb.effective = effective\r
+gb.organizationalUnit = organizational unit\r
+gb.organization = organization\r
+gb.locality = locality\r
+gb.stateProvince = state or province\r
+gb.countryCode = country code\r
+gb.properties = properties\r
+gb.issued = issued\r
+gb.expires = expires\r
+gb.expired = expired\r
+gb.expiring = expiring\r
+gb.revoked = revoked\r
+gb.serialNumber = serial number\r
+gb.certificates = certificates\r
+gb.newCertificate = new certificate\r
+gb.revokeCertificate = revoke certificate\r
+gb.sendEmail = send email\r
+gb.passwordHint = password hint\r
+gb.ok = ok\r
+gb.invalidExpirationDate = invalid expiration date!\r
+gb.passwordHintRequired = password hint required!\r
+gb.viewCertificate = view certificate\r
+gb.subject = subject\r
+gb.issuer = issuer\r
+gb.validFrom = valid from\r
+gb.validUntil = valid until\r
+gb.publicKey = public key\r
+gb.signatureAlgorithm = signature algorithm\r
+gb.sha1FingerPrint = SHA-1 Fingerprint\r
+gb.md5FingerPrint = MD5 Fingerprint\r
+gb.reason = reason\r
+gb.revokeCertificateReason = Please select a reason for certificate revocation\r
+gb.unspecified = unspecified\r
+gb.keyCompromise = key compromise\r
+gb.caCompromise = CA compromise\r
+gb.affiliationChanged = affiliation changed\r
+gb.superseded = superseded\r
+gb.cessationOfOperation = cessation of operation\r
+gb.privilegeWithdrawn = privilege withdrawn\r
+gb.time.inMinutes = in {0} mins\r
+gb.time.inHours = in {0} hours\r
+gb.time.inDays = in {0} days
\ No newline at end of file