Browse Source

Mostly functional Gitblit Certificate Authority tool

tags/v1.2.0
James Moger 11 years ago
parent
commit
4ad1ebce24

+ 148
- 68
build.xml View File

@@ -92,6 +92,7 @@
<property name="distribution.pomfile" value="${basedir}/pom.xml" />
<property name="fedclient.zipfile" value="fedclient-${gb.version}.zip" />
<property name="manager.zipfile" value="manager-${gb.version}.zip" />
<property name="authority.zipfile" value="authority-${gb.version}.zip" />
<property name="gbapi.zipfile" value="gbapi-${gb.version}.zip" />
<property name="express.zipfile" value="express-${gb.version}.zip" />
</target>
@@ -117,6 +118,7 @@
<copy todir="${basedir}/certs" overwrite="false">
<fileset dir="${basedir}/distrib">
<include name="authority.conf" />
<include name="*.tmpl" />
</fileset>
</copy>
@@ -200,12 +202,12 @@
<include name="authority.conf" />
</fileset>
</copy>
<!-- Build jar -->
<jar jarfile="${project.deploy.dir}/${project.jar}">
<fileset dir="${project.build.dir}">
<include name="**/*" />
<exclude name="com/gitblit/client/**" />
<exclude name="com/gitblit/client/**" />
</fileset>
<fileset dir="${project.resources.dir}">
<exclude name="thumbs.db" />
@@ -460,6 +462,7 @@
<exclude name="com/gitblit/GitBlitServer*.class" />
<exclude name="com/gitblit/Launcher*.class" />
<exclude name="com/gitblit/MakeCertificate*.class" />
<exclude name="com/gitblit/authority/**" />
</fileset>
</copy>
@@ -487,6 +490,7 @@
<exclude name="com/gitblit/tests/" />
<exclude name="com/gitblit/build/**" />
<exclude name="com/gitblit/client/**" />
<exclude name="com/gitblit/authority/**" />
<exclude name="com/gitblit/AddIndexedBranch*.class" />
<exclude name="com/gitblit/GitBlitServer*.class" />
<exclude name="com/gitblit/Launcher*.class" />
@@ -668,6 +672,7 @@
<exclude name="com/gitblit/GitBlitServer*.class" />
<exclude name="com/gitblit/Launcher*.class" />
<exclude name="com/gitblit/MakeCertificate*.class" />
<exclude name="com/gitblit/authority/**" />
</fileset>
</jar>
@@ -745,73 +750,138 @@
</zip>
</target>
<!--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build the stand-alone, Gitblit Authority
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildAuthority" depends="compile" description="Builds the stand-alone Gitblit Authority">
<echo>Building Gitblit Authority ${gb.version}</echo>
<genjar jarfile="authority-${gb.version}.jar">
<resource file="${basedir}/src/com/gitblit/client/splash.png" />
<resource file="${basedir}/resources/gitblt-favicon.png" />
<resource file="${basedir}/resources/user_16x16.png" />
<resource file="${basedir}/resources/users_16x16.png" />
<resource file="${basedir}/resources/rosette_16x16.png" />
<resource file="${basedir}/resources/vcard_16x16.png" />
<resource file="${basedir}/resources/settings_16x16.png" />
<resource file="${basedir}/resources/search-icon.png" />
<resource file="${basedir}/resources/blank.png" />
<resource file="${basedir}/resources/bullet_green.png" />
<resource file="${basedir}/resources/bullet_orange.png" />
<resource file="${basedir}/resources/bullet_red.png" />
<resource file="${basedir}/resources/bullet_white.png" />
<resource file="${basedir}/resources/bullet_delete.png" />
<resource file="${basedir}/resources/bullet_key.png" />
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp.properties" />
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_es.properties" />
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_ja.properties" />
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_ko.properties" />
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp_pl.properties" />
<class name="com.gitblit.authority.GitblitAuthorityLauncher" />
<classfilter>
<exclude name="org.apache." />
<exclude name="org.bouncycastle." />
<exclude name="org.eclipse." />
<exclude name="org.slf4j." />
<exclude name="com.beust." />
<exclude name="com.google." />
<exclude name="com.unboundid." />
</classfilter>
<classpath refid="master-classpath" />
<manifest>
<attribute name="Main-Class" value="com.gitblit.authority.GitblitAuthorityLauncher" />
<attribute name="SplashScreen-Image" value="splash.png" />
<attribute name="Specification-Version" value="${gb.version}" />
<attribute name="Release-Date" value="${gb.versionDate}" />
</manifest>
</genjar>
<!-- Build Authority Zip file -->
<zip destfile="${authority.zipfile}">
<fileset dir="${basedir}">
<include name="authority-${gb.version}.jar" />
<include name="LICENSE" />
<include name="NOTICE" />
</fileset>
<zipfileset dir="${basedir}/distrib" prefix="certs">
<include name="authority.conf" />
<include name="mail.tmpl" />
<include name="instructions.tmpl" />
</zipfileset>
</zip>
</target>
<!--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build the Gitblit API client library
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildApiLibrary" depends="compile" description="Builds the Gitblit RPC client library">
<echo>Building Gitblit API Library ${gb.version}</echo>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build the Gitblit API client library
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildApiLibrary" depends="compile" description="Builds the Gitblit RPC client library">
<echo>Building Gitblit API Library ${gb.version}</echo>
<!-- Build API Library jar -->
<genjar jarfile="gbapi-${gb.version}.jar">
<class name="com.gitblit.Keys" />
<class name="com.gitblit.client.GitblitClient" />
<classpath refid="master-classpath" />
<classfilter>
<exclude name="com.google.gson." />
<exclude name="com.sun.syndication." />
</classfilter>
<manifest>
<attribute name="Specification-Version" value="${gb.version}" />
<attribute name="Release-Date" value="${gb.versionDate}" />
</manifest>
</genjar>
<!-- Build API Library jar -->
<genjar jarfile="gbapi-${gb.version}.jar">
<class name="com.gitblit.Keys" />
<class name="com.gitblit.client.GitblitClient" />
<classpath refid="master-classpath" />
<classfilter>
<exclude name="com.google.gson." />
<exclude name="com.sun.syndication." />
</classfilter>
<manifest>
<attribute name="Specification-Version" value="${gb.version}" />
<attribute name="Release-Date" value="${gb.versionDate}" />
</manifest>
</genjar>
<!-- Build API sources jar -->
<zip destfile="gbapi-${gb.version}-sources.jar">
<fileset dir="${basedir}/src" defaultexcludes="yes">
<include name="com/gitblit/Constants.java"/>
<include name="com/gitblit/GitBlitException.java"/>
<include name="com/gitblit/Keys.java"/>
<include name="com/gitblit/client/**/*.java"/>
<include name="com/gitblit/models/**/*.java"/>
<include name="com/gitblit/utils/**/*.java"/>
</fileset>
</zip>
<!-- Build API JavaDoc jar -->
<javadoc destdir="${basedir}/javadoc">
<fileset dir="${basedir}/src" defaultexcludes="yes">
<include name="com/gitblit/Constants.java"/>
<include name="com/gitblit/GitBlitException.java"/>
<include name="com/gitblit/Keys.java"/>
<include name="com/gitblit/client/**/*.java"/>
<include name="com/gitblit/models/**/*.java"/>
<include name="com/gitblit/utils/**/*.java"/>
</fileset>
</javadoc>
<zip destfile="gbapi-${gb.version}-javadoc.jar">
<fileset dir="${basedir}/javadoc" />
</zip>
<!-- Build the API library zip file -->
<zip destfile="${gbapi.zipfile}">
<fileset dir="${basedir}">
<include name="gbapi-${gb.version}.jar" />
<include name="gbapi-${gb.version}-sources.jar" />
<include name="gbapi-${gb.version}-javadoc.jar" />
<include name="LICENSE" />
<include name="NOTICE" />
</fileset>
<fileset dir="${basedir}/ext">
<exclude name="src/**" />
<include name="gson*.jar" />
<include name="rome*.jar" />
<include name="jdom*.jar" />
</fileset>
</zip>
</target>
<!-- Build API sources jar -->
<zip destfile="gbapi-${gb.version}-sources.jar">
<fileset dir="${basedir}/src" defaultexcludes="yes">
<include name="com/gitblit/Constants.java"/>
<include name="com/gitblit/GitBlitException.java"/>
<include name="com/gitblit/Keys.java"/>
<include name="com/gitblit/client/**/*.java"/>
<include name="com/gitblit/models/**/*.java"/>
<include name="com/gitblit/utils/**/*.java"/>
</fileset>
</zip>
<!-- Build API JavaDoc jar -->
<javadoc destdir="${basedir}/javadoc">
<fileset dir="${basedir}/src" defaultexcludes="yes">
<include name="com/gitblit/Constants.java"/>
<include name="com/gitblit/GitBlitException.java"/>
<include name="com/gitblit/Keys.java"/>
<include name="com/gitblit/client/**/*.java"/>
<include name="com/gitblit/models/**/*.java"/>
<include name="com/gitblit/utils/**/*.java"/>
</fileset>
</javadoc>
<zip destfile="gbapi-${gb.version}-javadoc.jar">
<fileset dir="${basedir}/javadoc" />
</zip>
<!-- Build the API library zip file -->
<zip destfile="${gbapi.zipfile}">
<fileset dir="${basedir}">
<include name="gbapi-${gb.version}.jar" />
<include name="gbapi-${gb.version}-sources.jar" />
<include name="gbapi-${gb.version}-javadoc.jar" />
<include name="LICENSE" />
<include name="NOTICE" />
</fileset>
<fileset dir="${basedir}/ext">
<exclude name="src/**" />
<include name="gson*.jar" />
<include name="rome*.jar" />
<include name="jdom*.jar" />
</fileset>
</zip>
</target>
<!--
@@ -968,7 +1038,7 @@
Compile from source, publish binaries, and build & deploy site
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-->
<target name="buildAll" depends="buildGO,buildWAR,buildExpress,buildFederationClient,buildManager,buildApiLibrary,buildSite">
<target name="buildAll" depends="buildAuthority,buildGO,buildWAR,buildExpress,buildFederationClient,buildManager,buildApiLibrary,buildSite">
<!-- Cleanup -->
<delete dir="${project.build.dir}" />
<delete dir="${project.war.dir}" />
@@ -1046,6 +1116,16 @@
summary="Gitblit Manager v${gb.version} (Swing tool to remotely administer a Gitblit server)"
labels="Featured, Type-Package, OpSys-All" />
<!-- Upload Gitblit Authority -->
<gcupload
username="${googlecode.user}"
password="${googlecode.password}"
projectname="gitblit"
filename="${authority.zipfile}"
targetfilename="authority-${gb.version}.zip"
summary="Gitblit Authority v${gb.version} (Swing tool to manage client SSL certificates)"
labels="Featured, Type-Package, OpSys-All" />
<!-- Upload Gitblit API Library -->
<gcupload
username="${googlecode.user}"

+ 2
- 1
docs/04_releases.mkd View File

@@ -47,7 +47,7 @@ In order to fork a repository, the user account must have the *fork* permission
**New:** *git.garbageCollectionHour = 0*
**New:** *git.defaultGarbageCollectionThreshold = 500k*
**New:** *git.defaultGarbageCollectionPeriod = 7 days*
- Added support for X509 client certificate authentication (github/kevinanderson1).
- Added support for X509 client certificate authentication (github/kevinanderson1). (issue 106)
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.
**New:** *git.requireClientCertificates = false*
**New:** *git.enforceCertificateValidity = true*
@@ -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.
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.
**New:** *server.requireClientCertificates = false*
- Added Gitblit Certificate Authority, an X509 certificate generation tool for Gitblit GO to encourage use of client certificate authentication.
- Added setting to control length of shortened commit ids
**New:** *web.shortCommitIdLength=8*
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*.

BIN
resources/bullet_delete.png View File


BIN
resources/bullet_key.png View File


BIN
resources/rosette_16x16.png View File


BIN
resources/vcard_16x16.png View File


+ 20
- 0
src/com/gitblit/authority/CertificateStatus.java View File

@@ -0,0 +1,20 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
public enum CertificateStatus {
unknown, ok, expiring, expired, revoked
}

+ 82
- 0
src/com/gitblit/authority/CertificateStatusRenderer.java View File

@@ -0,0 +1,82 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import com.gitblit.client.Translation;
/**
* Displays a subscribed icon on the left of the repository name, if there is at
* least one subscribed branch.
*
* @author James Moger
*
*/
public class CertificateStatusRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
private final ImageIcon unknownIcon;
private final ImageIcon revokedIcon;
private final ImageIcon expiredIcon;
private final ImageIcon expiringIcon;
private final ImageIcon okIcon;
public CertificateStatusRenderer() {
super();
unknownIcon = new ImageIcon(getClass().getResource("/bullet_white.png"));
revokedIcon = new ImageIcon(getClass().getResource("/bullet_delete.png"));
expiredIcon = new ImageIcon(getClass().getResource("/bullet_red.png"));
expiringIcon = new ImageIcon(getClass().getResource("/bullet_orange.png"));
okIcon = new ImageIcon(getClass().getResource("/bullet_green.png"));
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (value instanceof CertificateStatus) {
CertificateStatus status = (CertificateStatus) value;
switch(status) {
case revoked:
setText(Translation.get("gb.revoked"));
setIcon(revokedIcon);
break;
case expiring:
setText(Translation.get("gb.expiring"));
setIcon(expiringIcon);
break;
case expired:
setText(Translation.get("gb.expired"));
setIcon(expiredIcon);
break;
case unknown:
setText("");
setIcon(unknownIcon);
break;
default:
setText(Translation.get("gb.ok"));
setIcon(okIcon);
break;
}
}
return this;
}
}

+ 166
- 0
src/com/gitblit/authority/CertificatesTableModel.java View File

@@ -0,0 +1,166 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.math.BigInteger;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import javax.swing.table.AbstractTableModel;
import com.gitblit.client.Translation;
import com.gitblit.utils.X509Utils.RevocationReason;
/**
* Table model of a list of user certificate models.
*
* @author James Moger
*
*/
public class CertificatesTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
UserCertificateModel ucm;
enum Columns {
SerialNumber, Status, Reason, Issued, Expires;
@Override
public String toString() {
return name().replace('_', ' ');
}
}
public CertificatesTableModel() {
}
@Override
public int getRowCount() {
return ucm == null || ucm.certs == null ? 0 : ucm.certs.size();
}
@Override
public int getColumnCount() {
return Columns.values().length;
}
@Override
public String getColumnName(int column) {
Columns col = Columns.values()[column];
switch (col) {
case SerialNumber:
return Translation.get("gb.serialNumber");
case Issued:
return Translation.get("gb.issued");
case Expires:
return Translation.get("gb.expires");
case Status:
return Translation.get("gb.status");
case Reason:
return Translation.get("gb.reason");
}
return "";
}
/**
* Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
*
* @param columnIndex
* the column being queried
* @return the Object.class
*/
public Class<?> getColumnClass(int columnIndex) {
Columns col = Columns.values()[columnIndex];
switch (col) {
case Status:
return CertificateStatus.class;
case Issued:
return Date.class;
case Expires:
return Date.class;
case SerialNumber:
return BigInteger.class;
default:
return String.class;
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
Columns col = Columns.values()[columnIndex];
switch (col) {
default:
return false;
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
X509Certificate cert = ucm.certs.get(rowIndex);
Columns col = Columns.values()[columnIndex];
switch (col) {
case Status:
return ucm.getStatus(cert);
case SerialNumber:
return cert.getSerialNumber();
case Issued:
return cert.getNotBefore();
case Expires:
return cert.getNotAfter();
case Reason:
if (ucm.getStatus(cert).equals(CertificateStatus.revoked)) {
RevocationReason r = ucm.getRevocationReason(cert.getSerialNumber());
return Translation.get("gb." + r.name());
}
}
return null;
}
public X509Certificate get(int modelRow) {
return ucm.certs.get(modelRow);
}
public void setUserCertificateModel(UserCertificateModel ucm) {
this.ucm = ucm;
Collections.sort(ucm.certs, new Comparator<X509Certificate>() {
@Override
public int compare(X509Certificate o1, X509Certificate o2) {
// sort by issue date in reverse chronological order
int result = o2.getNotBefore().compareTo(o1.getNotBefore());
if (result == 0) {
// same issue date, show expiring first
boolean r1 = CertificatesTableModel.this.ucm.isRevoked(o1.getSerialNumber());
boolean r2 = CertificatesTableModel.this.ucm.isRevoked(o2.getSerialNumber());
if ((r1 && r2) || (!r1 && !r2)) {
// both revoked or both not revoked
// chronlogical order by expiration dates
result = o1.getNotAfter().compareTo(o2.getNotAfter());
} else if (r1) {
// r1 is revoked, r2 first
return 1;
} else {
// r2 is revoked, r1 first
return -1;
}
}
return result;
}
});
}
}

+ 1
- 1
src/com/gitblit/authority/GitblitAuthority.java View File

@@ -136,7 +136,7 @@ public class GitblitAuthority extends JFrame {
public void initialize() {
setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
setTitle("Gitblit PKI Authority v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");
setTitle("Gitblit Certificate Authority v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");
setContentPane(getUI());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addWindowListener(new WindowAdapter() {

+ 113
- 0
src/com/gitblit/authority/GitblitAuthorityLauncher.java View File

@@ -0,0 +1,113 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.SplashScreen;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import com.gitblit.Constants;
import com.gitblit.Launcher;
import com.gitblit.build.Build;
import com.gitblit.build.Build.DownloadListener;
import com.gitblit.client.Translation;
/**
* Downloads dependencies and launches Gitblit Authority.
*
* @author James Moger
*
*/
public class GitblitAuthorityLauncher {
public static void main(String[] args) {
final SplashScreen splash = SplashScreen.getSplashScreen();
DownloadListener downloadListener = new DownloadListener() {
@Override
public void downloading(String name) {
updateSplash(splash, Translation.get("gb.downloading") + " " + name);
}
};
// download authority runtime dependencies
Build.authority(downloadListener);
File libFolder = new File("ext");
List<File> jars = Launcher.findJars(libFolder.getAbsoluteFile());
// sort the jars by name and then reverse the order so the newer version
// of the library gets loaded in the event that this is an upgrade
Collections.sort(jars);
Collections.reverse(jars);
for (File jar : jars) {
try {
updateSplash(splash, Translation.get("gb.loading") + " " + jar.getName() + "...");
Launcher.addJarFile(jar);
} catch (IOException e) {
}
}
updateSplash(splash, Translation.get("gb.starting") + " Gitblit Authority...");
GitblitAuthority.main(args);
}
private static void updateSplash(final SplashScreen splash, final String string) {
if (splash == null) {
return;
}
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
Graphics2D g = splash.createGraphics();
if (g != null) {
// Splash is 320x120
FontMetrics fm = g.getFontMetrics();
// paint startup status
g.setColor(Color.darkGray);
int h = fm.getHeight() + fm.getMaxDescent();
int x = 5;
int y = 115;
int w = 320 - 2 * x;
g.fillRect(x, y - h, w, h);
g.setColor(Color.lightGray);
g.drawRect(x, y - h, w, h);
g.setColor(Color.WHITE);
int xw = fm.stringWidth(string);
g.drawString(string, x + ((w - xw) / 2), y - 5);
// paint version
String ver = "v" + Constants.VERSION;
int vw = g.getFontMetrics().stringWidth(ver);
g.drawString(ver, 320 - vw - 5, 34);
g.dispose();
splash.update();
}
}
});
} catch (Throwable t) {
t.printStackTrace();
}
}
}

+ 164
- 0
src/com/gitblit/authority/NewClientCertificateDialog.java View File

@@ -0,0 +1,164 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import org.bouncycastle.util.Arrays;
import com.gitblit.client.HeaderPanel;
import com.gitblit.client.Translation;
import com.gitblit.utils.StringUtils;
import com.toedter.calendar.JDateChooser;
public class NewClientCertificateDialog extends JDialog {
private static final long serialVersionUID = 1L;
JDateChooser expirationDate;
JPasswordField pw1;
JPasswordField pw2;
JTextField hint;
JCheckBox sendEmail;
boolean isCanceled = true;
public NewClientCertificateDialog(Frame owner, String displayname, Date defaultExpiration) {
super(owner);
setTitle(Translation.get("gb.newCertificate"));
JPanel content = new JPanel(new BorderLayout(5, 5)) {
private static final long serialVersionUID = 1L;
@Override
public Insets getInsets() {
return Utils.INSETS;
}
};
content.add(new HeaderPanel(Translation.get("gb.newCertificate") + " (" + displayname + ")", "rosette_16x16.png"), BorderLayout.NORTH);
expirationDate = new JDateChooser(defaultExpiration);
pw1 = new JPasswordField(20);
pw2 = new JPasswordField(20);
hint = new JTextField(20);
sendEmail = new JCheckBox(Translation.get("gb.sendEmail"));
JPanel panel = new JPanel(new GridLayout(0, 2, 5, 5));
panel.add(new JLabel(Translation.get("gb.expires")));
panel.add(expirationDate);
panel.add(new JLabel(Translation.get("gb.password")));
panel.add(pw1);
panel.add(new JLabel(Translation.get("gb.confirmPassword")));
panel.add(pw2);
panel.add(new JLabel(Translation.get("gb.passwordHint")));
panel.add(hint);
panel.add(new JLabel(""));
panel.add(sendEmail);
content.add(panel, BorderLayout.CENTER);
JButton ok = new JButton(Translation.get("gb.ok"));
ok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (validateInputs()) {
isCanceled = false;
setVisible(false);
}
}
});
JButton cancel = new JButton(Translation.get("gb.cancel"));
cancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
isCanceled = true;
setVisible(false);
}
});
JPanel controls = new JPanel();
controls.add(ok);
controls.add(cancel);
content.add(controls, BorderLayout.SOUTH);
getContentPane().add(content, BorderLayout.CENTER);
pack();
setLocationRelativeTo(owner);
}
private boolean validateInputs() {
if (getExpiration().getTime() < System.currentTimeMillis()) {
// expires before now
JOptionPane.showMessageDialog(this, Translation.get("gb.invalidExpiraitonDate"),
Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
return false;
}
if (pw1.getPassword().length == 0 || !Arrays.areEqual(pw1.getPassword(), pw2.getPassword())) {
// password mismatch
JOptionPane.showMessageDialog(this, Translation.get("gb.passwordsDoNotMatch"),
Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
return false;
}
if (StringUtils.isEmpty(getPasswordHint())) {
// must have hint
JOptionPane.showMessageDialog(this, Translation.get("gb.passwordHintRequired"),
Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
return false;
}
return true;
}
public String getPassword() {
return new String(pw1.getPassword());
}
public String getPasswordHint() {
return hint.getText();
}
public Date getExpiration() {
return expirationDate.getDate();
}
public boolean sendEmail() {
return sendEmail.isSelected();
}
public boolean isCanceled() {
return isCanceled;
}
}

+ 334
- 0
src/com/gitblit/authority/UserCertificatePanel.java View File

@@ -0,0 +1,334 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.security.cert.X509Certificate;
import java.util.Date;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import com.gitblit.client.HeaderPanel;
import com.gitblit.client.Translation;
import com.gitblit.models.UserModel;
import com.gitblit.utils.X509Utils.RevocationReason;
import com.gitblit.utils.X509Utils.X509Metadata;
public abstract class UserCertificatePanel extends JPanel {
private static final long serialVersionUID = 1L;
private Frame owner;
private UserCertificateModel ucm;
private JTextField displayname;
private JTextField username;
private JTextField emailAddress;
private JTextField organizationalUnit;
private JTextField organization;
private JTextField locality;
private JTextField stateProvince;
private JTextField countryCode;
private CertificatesTableModel tableModel;
private JButton saveUserButton;
private JButton editUserButton;
private JButton newCertificateButton;
private JButton revokeCertificateButton;
private JTable table;
public UserCertificatePanel(Frame owner) {
super(new BorderLayout());
this.owner = owner;
displayname = new JTextField(20);
username = new JTextField(20);
username.setEditable(false);
emailAddress = new JTextField(20);
organizationalUnit = new JTextField(20);
organization = new JTextField(20);
locality = new JTextField(20);
stateProvince = new JTextField(20);
countryCode = new JTextField(20);
JPanel fields = new JPanel(new GridLayout(0, 1, 5, 5));
fields.add(newFieldPanel(Translation.get("gb.displayName"), displayname));
fields.add(newFieldPanel(Translation.get("gb.username") + " (CN)", username));
fields.add(newFieldPanel(Translation.get("gb.emailAddress") + " (E)", emailAddress));
fields.add(newFieldPanel(Translation.get("gb.organizationalUnit") + " (OU)", organizationalUnit));
fields.add(newFieldPanel(Translation.get("gb.organization") + " (O)", organization));
fields.add(newFieldPanel(Translation.get("gb.locality") + " (L)", locality));
fields.add(newFieldPanel(Translation.get("gb.stateProvince") + " (ST)", stateProvince));
fields.add(newFieldPanel(Translation.get("gb.countryCode") + " (C)", countryCode));
JPanel fp = new JPanel(new BorderLayout(5, 5));
fp.add(fields, BorderLayout.NORTH);
JPanel fieldsPanel = new JPanel(new BorderLayout());
fieldsPanel.add(new HeaderPanel(Translation.get("gb.properties"), "vcard_16x16.png"), BorderLayout.NORTH);
fieldsPanel.add(fp, BorderLayout.CENTER);
saveUserButton = new JButton(Translation.get("gb.save"));
saveUserButton.setEnabled(false);
saveUserButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setEditable(false);
String username = ucm.user.username;
updateUser();
saveUser(username, ucm);
}
});
editUserButton = new JButton(Translation.get("gb.edit"));
editUserButton.setEnabled(false);
editUserButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setEditable(true);
}
});
JPanel userControls = new JPanel(new FlowLayout(FlowLayout.LEFT));
userControls.add(editUserButton);
userControls.add(saveUserButton);
fieldsPanel.add(userControls, BorderLayout.SOUTH);
JPanel certificatesPanel = new JPanel(new BorderLayout());
certificatesPanel.add(new HeaderPanel(Translation.get("gb.certificates"), "rosette_16x16.png"), BorderLayout.NORTH);
tableModel = new CertificatesTableModel();
table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
table.setRowSorter(new TableRowSorter<CertificatesTableModel>(tableModel));
table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer());
table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
boolean enable = false;
int row = table.getSelectedRow();
if (row > -1) {
int modelIndex = table.convertRowIndexToModel(row);
X509Certificate cert = tableModel.get(modelIndex);
enable = !ucm.isRevoked(cert.getSerialNumber());
}
revokeCertificateButton.setEnabled(enable);
}
});
table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
int row = table.rowAtPoint(e.getPoint());
int modelIndex = table.convertRowIndexToModel(row);
X509Certificate cert = tableModel.get(modelIndex);
X509CertificateViewer viewer = new X509CertificateViewer(UserCertificatePanel.this.owner, cert);
viewer.setVisible(true);
}
}
});
certificatesPanel.add(new JScrollPane(table), BorderLayout.CENTER);
newCertificateButton = new JButton(Translation.get("gb.newCertificate"));
newCertificateButton.setEnabled(false);
newCertificateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
if (saveUserButton.isEnabled()) {
// save changes
String username = ucm.user.username;
setEditable(false);
updateUser();
saveUser(username, ucm);
}
NewClientCertificateDialog dialog = new NewClientCertificateDialog(UserCertificatePanel.this.owner,
ucm.user.getDisplayName(), getDefaultExpiration());
dialog.setModal(true);
dialog.setVisible(true);
if (dialog.isCanceled()) {
return;
}
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
UserModel user = ucm.user;
X509Metadata metadata = new X509Metadata(user.username, dialog.getPassword());
metadata.userDisplayname = user.getDisplayName();
metadata.emailAddress = user.emailAddress;
metadata.passwordHint = dialog.getPasswordHint();
metadata.notAfter = dialog.getExpiration();
newCertificate(ucm, metadata, dialog.sendEmail());
} catch (Exception x) {
Utils.showException(UserCertificatePanel.this, x);
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
});
revokeCertificateButton = new JButton(Translation.get("gb.revokeCertificate"));
revokeCertificateButton.setEnabled(false);
revokeCertificateButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
int row = table.getSelectedRow();
if (row < 0) {
return;
}
int modelIndex = table.convertRowIndexToModel(row);
X509Certificate cert = tableModel.get(modelIndex);
String [] choices = new String[RevocationReason.reasons.length];
for (int i = 0; i < choices.length; i++) {
choices[i] = Translation.get("gb." + RevocationReason.reasons[i].name());
}
Object choice = JOptionPane.showInputDialog(UserCertificatePanel.this.owner,
Translation.get("gb.revokeCertificateReason"), Translation.get("gb.revokeCertificate"),
JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("/rosette_16x16.png")), choices, Translation.get("gb.unspecified"));
if (choice == null) {
return;
}
RevocationReason reason = RevocationReason.unspecified;
for (int i = 0 ; i < choices.length; i++) {
if (choices[i].equals(choice)) {
reason = RevocationReason.reasons[i];
break;
}
}
if (!ucm.isRevoked(cert.getSerialNumber())) {
if (ucm.certs.size() == 1) {
// no other certificates
ucm.expires = null;
} else {
// determine new expires date for user
Date newExpires = null;
for (X509Certificate c : ucm.certs) {
if (!c.equals(cert)) {
if (!ucm.isRevoked(c.getSerialNumber())) {
if (newExpires == null || c.getNotAfter().after(newExpires)) {
newExpires = c.getNotAfter();
}
}
}
}
ucm.expires = newExpires;
}
revoke(ucm, cert, reason);
}
} catch (Exception x) {
Utils.showException(UserCertificatePanel.this, x);
} finally {
setCursor(Cursor.getDefaultCursor());
}
}
});
JPanel certificateControls = new JPanel(new FlowLayout(FlowLayout.LEFT));
certificateControls.add(newCertificateButton);
certificateControls.add(revokeCertificateButton);
certificatesPanel.add(certificateControls, BorderLayout.SOUTH);
add(fieldsPanel, BorderLayout.NORTH);
add(certificatesPanel, BorderLayout.CENTER);
setEditable(false);
}
private JPanel newFieldPanel(String label, Component c) {
JLabel jlabel = new JLabel(label);
jlabel.setPreferredSize(new Dimension(150, 20));
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
panel.add(jlabel);
panel.add(c);
return panel;
}
public void setUserCertificateModel(UserCertificateModel ucm) {
this.ucm = ucm;
setEditable(false);
displayname.setText(ucm.user.getDisplayName());
username.setText(ucm.user.username);
emailAddress.setText(ucm.user.emailAddress);
organizationalUnit.setText(ucm.user.organizationalUnit);
organization.setText(ucm.user.organization);
locality.setText(ucm.user.locality);
stateProvince.setText(ucm.user.stateProvince);
countryCode.setText(ucm.user.countryCode);
tableModel.setUserCertificateModel(ucm);
tableModel.fireTableDataChanged();
}
public void setEditable(boolean editable) {
displayname.setEditable(editable);
// username.setEditable(editable);
emailAddress.setEditable(editable);
organizationalUnit.setEditable(editable);
organization.setEditable(editable);
locality.setEditable(editable);
stateProvince.setEditable(editable);
countryCode.setEditable(editable);
editUserButton.setEnabled(!editable && ucm != null);
saveUserButton.setEnabled(editable && ucm != null);
newCertificateButton.setEnabled(ucm != null);
revokeCertificateButton.setEnabled(false);
}
private void updateUser() {
ucm.user.displayName = displayname.getText();
ucm.user.username = username.getText();
ucm.user.emailAddress = emailAddress.getText();
ucm.user.organizationalUnit = organizationalUnit.getText();
ucm.user.organization = organization.getText();
ucm.user.locality = locality.getText();
ucm.user.stateProvince = stateProvince.getText();
ucm.user.countryCode = countryCode.getText();
}
public abstract Date getDefaultExpiration();
public abstract void saveUser(String username, UserCertificateModel ucm);
public abstract void newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail);
public abstract void revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason);
}

+ 131
- 0
src/com/gitblit/authority/UserCertificateTableModel.java View File

@@ -0,0 +1,131 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.swing.table.AbstractTableModel;
import com.gitblit.client.Translation;
/**
* Table model of a list of user certificate models.
*
* @author James Moger
*
*/
public class UserCertificateTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
List<UserCertificateModel> list;
enum Columns {
Username, DisplayName, Status, Expires;
@Override
public String toString() {
return name().replace('_', ' ');
}
}
public UserCertificateTableModel() {
this(new ArrayList<UserCertificateModel>());
}
public UserCertificateTableModel(List<UserCertificateModel> list) {
this.list = list;
Collections.sort(this.list);
}
@Override
public int getRowCount() {
return list.size();
}
@Override
public int getColumnCount() {
return Columns.values().length;
}
@Override
public String getColumnName(int column) {
Columns col = Columns.values()[column];
switch (col) {
case Username:
return Translation.get("gb.username");
case DisplayName:
return Translation.get("gb.displayName");
case Expires:
return Translation.get("gb.expires");
case Status:
return Translation.get("gb.status");
}
return "";
}
/**
* Returns <code>Object.class</code> regardless of <code>columnIndex</code>.
*
* @param columnIndex
* the column being queried
* @return the Object.class
*/
public Class<?> getColumnClass(int columnIndex) {
Columns col = Columns.values()[columnIndex];
switch (col) {
case Expires:
return Date.class;
case Status:
return CertificateStatus.class;
default:
return String.class;
}
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
Columns col = Columns.values()[columnIndex];
switch (col) {
default:
return false;
}
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
UserCertificateModel model = list.get(rowIndex);
Columns col = Columns.values()[columnIndex];
switch (col) {
case Username:
return model.user.username;
case DisplayName:
return model.user.getDisplayName();
case Expires:
return model.expires;
case Status:
return model.getStatus();
}
return null;
}
public UserCertificateModel get(int modelRow) {
return list.get(modelRow);
}
}

+ 101
- 0
src/com/gitblit/authority/Utils.java View File

@@ -0,0 +1,101 @@
package com.gitblit.authority;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import com.gitblit.client.DateCellRenderer;
import com.gitblit.client.Translation;
public class Utils {
public final static int MARGIN = 5;
public final static Insets INSETS = new Insets(MARGIN, MARGIN, MARGIN, MARGIN);
public final static String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm";
public final static String DATE_FORMAT = "yyyy-MM-dd";
public static JTable newTable(TableModel model, String datePattern) {
JTable table = new JTable(model);
table.setRowHeight(table.getFont().getSize() + 8);
table.setCellSelectionEnabled(false);
table.setRowSelectionAllowed(true);
table.getTableHeader().setReorderingAllowed(false);
table.setGridColor(new Color(0xd9d9d9));
table.setBackground(Color.white);
table.setDefaultRenderer(Date.class,
new DateCellRenderer(datePattern, Color.orange.darker()));
return table;
}
public static void showException(Component c, Throwable t) {
StringWriter writer = new StringWriter();
t.printStackTrace(new PrintWriter(writer));
String stacktrace = writer.toString();
try {
writer.close();
} catch (Throwable x) {
}
JTextArea textArea = new JTextArea(stacktrace);
textArea.setFont(new Font("monospaced", Font.PLAIN, 11));
JScrollPane jsp = new JScrollPane(textArea);
jsp.setPreferredSize(new Dimension(800, 400));
JOptionPane.showMessageDialog(c, jsp, Translation.get("gb.error"),
JOptionPane.ERROR_MESSAGE);
}
public static void packColumns(JTable table, int margin) {
for (int c = 0; c < table.getColumnCount(); c++) {
packColumn(table, c, 4);
}
}
// Sets the preferred width of the visible column specified by vColIndex.
// The column will be just wide enough to show the column head and the
// widest cell in the column. margin pixels are added to the left and right
// (resulting in an additional width of 2*margin pixels).
private static void packColumn(JTable table, int vColIndex, int margin) {
DefaultTableColumnModel colModel = (DefaultTableColumnModel) table.getColumnModel();
TableColumn col = colModel.getColumn(vColIndex);
int width = 0;
// Get width of column header
TableCellRenderer renderer = col.getHeaderRenderer();
if (renderer == null) {
renderer = table.getTableHeader().getDefaultRenderer();
}
Component comp = renderer.getTableCellRendererComponent(table, col.getHeaderValue(), false,
false, 0, 0);
width = comp.getPreferredSize().width;
// Get maximum width of column data
for (int r = 0; r < table.getRowCount(); r++) {
renderer = table.getCellRenderer(r, vColIndex);
comp = renderer.getTableCellRendererComponent(table, table.getValueAt(r, vColIndex),
false, false, r, vColIndex);
width = Math.max(width, comp.getPreferredSize().width);
}
// Add margin
width += 2 * margin;
// Set the width
col.setPreferredWidth(width);
}
}

+ 129
- 0
src/com/gitblit/authority/X509CertificateViewer.java View File

@@ -0,0 +1,129 @@
/*
* Copyright 2012 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.authority;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import com.gitblit.client.HeaderPanel;
import com.gitblit.client.Translation;
import com.gitblit.utils.StringUtils;
public class X509CertificateViewer extends JDialog {
private static final long serialVersionUID = 1L;
public X509CertificateViewer(Frame owner, X509Certificate cert) {
super(owner);
setTitle(Translation.get("gb.viewCertificate"));
JPanel content = new JPanel(new BorderLayout(5, 5)) {
private static final long serialVersionUID = 1L;
@Override
public Insets getInsets() {
return Utils.INSETS;
}
};
content.add(new HeaderPanel("certificiate", "rosette_16x16.png"), BorderLayout.NORTH);
DateFormat df = DateFormat.getDateTimeInstance();
int l1 = 15;
int l2 = 25;
int l3 = 45;
JPanel panel = new JPanel(new GridLayout(0, 1, 0, 10));
panel.add(newField(Translation.get("gb.version"), "" + cert.getVersion(), 3));
panel.add(newField(Translation.get("gb.subject"), cert.getSubjectDN().getName(), l3));
panel.add(newField(Translation.get("gb.issuer"), cert.getIssuerDN().getName(), l3));
panel.add(newField(Translation.get("gb.serialNumber"), "0x" + cert.getSerialNumber().toString(16), l2));
panel.add(newField(Translation.get("gb.serialNumber"), cert.getSerialNumber().toString(), l2));
panel.add(newField(Translation.get("gb.validFrom"), df.format(cert.getNotBefore()), l2));
panel.add(newField(Translation.get("gb.validUntil"), df.format(cert.getNotAfter()), l2));
panel.add(newField(Translation.get("gb.publicKey"), cert.getPublicKey().getAlgorithm(), l1));
panel.add(newField(Translation.get("gb.signatureAlgorithm"), cert.getSigAlgName(), l1));
try {
panel.add(newField(Translation.get("gb.sha1FingerPrint"), fingerprint(StringUtils.getSHA1(cert.getEncoded())), l3));
} catch (CertificateEncodingException e1) {
}
try {
panel.add(newField(Translation.get("gb.md5FingerPrint"), fingerprint(StringUtils.getMD5(cert.getEncoded())), l3));
} catch (CertificateEncodingException e1) {
}
content.add(panel, BorderLayout.CENTER);
JButton ok = new JButton(Translation.get("gb.ok"));
ok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
JPanel controls = new JPanel();
controls.add(ok);
content.add(controls, BorderLayout.SOUTH);
getContentPane().add(content, BorderLayout.CENTER);
pack();
setLocationRelativeTo(owner);
}
private JPanel newField(String label, String value, int cols) {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
JLabel lbl = new JLabel(label);
lbl.setHorizontalAlignment(SwingConstants.RIGHT);
lbl.setPreferredSize(new Dimension(125, 20));
panel.add(lbl);
JTextField tf = new JTextField(value, cols);
tf.setCaretPosition(0);
tf.setEditable(false);
panel.add(tf);
return panel;
}
private String fingerprint(String value) {
value = value.toUpperCase();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < value.length(); i += 2) {
sb.append(value.charAt(i));
sb.append(value.charAt(i + 1));
sb.append(':');
}
sb.setLength(sb.length() - 1);
return sb.toString();
}
}

+ 7
- 3
src/com/gitblit/client/DateCellRenderer.java View File

@@ -24,8 +24,6 @@ import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import com.gitblit.utils.TimeUtils;
/**
* Time ago cell renderer with real date tooltip.
*
@@ -55,7 +53,13 @@ public class DateCellRenderer extends DefaultTableCellRenderer {
title = "--";
dateString = "never";
} else {
title = Translation.getTimeUtils().timeAgo(date);
if (date.getTime() - System.currentTimeMillis() > 0) {
// future
title = Translation.getTimeUtils().inFuture(date);
} else {
// past
title = Translation.getTimeUtils().timeAgo(date);
}
dateString = new SimpleDateFormat(pattern).format((Date) value);
}

+ 15
- 3
src/com/gitblit/utils/StringUtils.java View File

@@ -262,14 +262,26 @@ public class StringUtils {
* @return md5 of the string
*/
public static String getMD5(String string) {
try {
return getMD5(string.getBytes("iso-8859-1"));
} catch (UnsupportedEncodingException u) {
throw new RuntimeException(u);
}
}
/**
* Calculates the MD5 of the string.
*
* @param string
* @return md5 of the string
*/
public static String getMD5(byte [] bytes) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(string.getBytes("iso-8859-1"));
md.update(bytes);
byte[] digest = md.digest();
return toHex(digest);
} catch (UnsupportedEncodingException u) {
throw new RuntimeException(u);
} catch (NoSuchAlgorithmException t) {
throw new RuntimeException(t);
}

+ 16
- 0
src/com/gitblit/utils/TimeUtils.java View File

@@ -268,6 +268,22 @@ public class TimeUtils {
}
}
public String inFuture(Date date) {
long diff = date.getTime() - System.currentTimeMillis();
if (diff > ONEDAY) {
double days = ((double) diff)/ONEDAY;
return translate((int) Math.round(days), "gb.time.inDays", "in {0} days");
} else {
double hours = ((double) diff)/ONEHOUR;
if (hours > 2) {
return translate((int) Math.round(hours), "gb.time.inHours", "in {0} hours");
} else {
int mins = (int) (diff/MIN);
return translate(mins, "gb.time.inMinutes", "in {0} minutes");
}
}
}
private String translate(String key, String defaultValue) {
String value = defaultValue;
if (translation != null && translation.containsKey(key)) {

+ 41
- 0
src/com/gitblit/wicket/GitBlitWebApp.properties View File

@@ -374,3 +374,44 @@ gb.missingPermission = the repository for this permission is missing!
gb.mutable = mutable
gb.specified = specified
gb.effective = effective
gb.organizationalUnit = organizational unit
gb.organization = organization
gb.locality = locality
gb.stateProvince = state or province
gb.countryCode = country code
gb.properties = properties
gb.issued = issued
gb.expires = expires
gb.expired = expired
gb.expiring = expiring
gb.revoked = revoked
gb.serialNumber = serial number
gb.certificates = certificates
gb.newCertificate = new certificate
gb.revokeCertificate = revoke certificate
gb.sendEmail = send email
gb.passwordHint = password hint
gb.ok = ok
gb.invalidExpirationDate = invalid expiration date!
gb.passwordHintRequired = password hint required!
gb.viewCertificate = view certificate
gb.subject = subject
gb.issuer = issuer
gb.validFrom = valid from
gb.validUntil = valid until
gb.publicKey = public key
gb.signatureAlgorithm = signature algorithm
gb.sha1FingerPrint = SHA-1 Fingerprint
gb.md5FingerPrint = MD5 Fingerprint
gb.reason = reason
gb.revokeCertificateReason = Please select a reason for certificate revocation
gb.unspecified = unspecified
gb.keyCompromise = key compromise
gb.caCompromise = CA compromise
gb.affiliationChanged = affiliation changed
gb.superseded = superseded
gb.cessationOfOperation = cessation of operation
gb.privilegeWithdrawn = privilege withdrawn
gb.time.inMinutes = in {0} mins
gb.time.inHours = in {0} hours
gb.time.inDays = in {0} days

Loading…
Cancel
Save