@@ -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}" |
@@ -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*. |
@@ -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 | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
}); | |||
} | |||
} |
@@ -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() { |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -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); | |||
} |
@@ -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)) { |
@@ -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 |