<property name="distribution.pomfile" value="${basedir}/pom.xml" /> | <property name="distribution.pomfile" value="${basedir}/pom.xml" /> | ||||
<property name="fedclient.zipfile" value="fedclient-${gb.version}.zip" /> | <property name="fedclient.zipfile" value="fedclient-${gb.version}.zip" /> | ||||
<property name="manager.zipfile" value="manager-${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="gbapi.zipfile" value="gbapi-${gb.version}.zip" /> | ||||
<property name="express.zipfile" value="express-${gb.version}.zip" /> | <property name="express.zipfile" value="express-${gb.version}.zip" /> | ||||
</target> | </target> | ||||
<copy todir="${basedir}/certs" overwrite="false"> | <copy todir="${basedir}/certs" overwrite="false"> | ||||
<fileset dir="${basedir}/distrib"> | <fileset dir="${basedir}/distrib"> | ||||
<include name="authority.conf" /> | <include name="authority.conf" /> | ||||
<include name="*.tmpl" /> | |||||
</fileset> | </fileset> | ||||
</copy> | </copy> | ||||
<include name="authority.conf" /> | <include name="authority.conf" /> | ||||
</fileset> | </fileset> | ||||
</copy> | </copy> | ||||
<!-- Build jar --> | <!-- Build jar --> | ||||
<jar jarfile="${project.deploy.dir}/${project.jar}"> | <jar jarfile="${project.deploy.dir}/${project.jar}"> | ||||
<fileset dir="${project.build.dir}"> | <fileset dir="${project.build.dir}"> | ||||
<include name="**/*" /> | <include name="**/*" /> | ||||
<exclude name="com/gitblit/client/**" /> | |||||
<exclude name="com/gitblit/client/**" /> | |||||
</fileset> | </fileset> | ||||
<fileset dir="${project.resources.dir}"> | <fileset dir="${project.resources.dir}"> | ||||
<exclude name="thumbs.db" /> | <exclude name="thumbs.db" /> | ||||
<exclude name="com/gitblit/GitBlitServer*.class" /> | <exclude name="com/gitblit/GitBlitServer*.class" /> | ||||
<exclude name="com/gitblit/Launcher*.class" /> | <exclude name="com/gitblit/Launcher*.class" /> | ||||
<exclude name="com/gitblit/MakeCertificate*.class" /> | <exclude name="com/gitblit/MakeCertificate*.class" /> | ||||
<exclude name="com/gitblit/authority/**" /> | |||||
</fileset> | </fileset> | ||||
</copy> | </copy> | ||||
<exclude name="com/gitblit/tests/" /> | <exclude name="com/gitblit/tests/" /> | ||||
<exclude name="com/gitblit/build/**" /> | <exclude name="com/gitblit/build/**" /> | ||||
<exclude name="com/gitblit/client/**" /> | <exclude name="com/gitblit/client/**" /> | ||||
<exclude name="com/gitblit/authority/**" /> | |||||
<exclude name="com/gitblit/AddIndexedBranch*.class" /> | <exclude name="com/gitblit/AddIndexedBranch*.class" /> | ||||
<exclude name="com/gitblit/GitBlitServer*.class" /> | <exclude name="com/gitblit/GitBlitServer*.class" /> | ||||
<exclude name="com/gitblit/Launcher*.class" /> | <exclude name="com/gitblit/Launcher*.class" /> | ||||
<exclude name="com/gitblit/GitBlitServer*.class" /> | <exclude name="com/gitblit/GitBlitServer*.class" /> | ||||
<exclude name="com/gitblit/Launcher*.class" /> | <exclude name="com/gitblit/Launcher*.class" /> | ||||
<exclude name="com/gitblit/MakeCertificate*.class" /> | <exclude name="com/gitblit/MakeCertificate*.class" /> | ||||
<exclude name="com/gitblit/authority/**" /> | |||||
</fileset> | </fileset> | ||||
</jar> | </jar> | ||||
</zip> | </zip> | ||||
</target> | </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> | |||||
<!-- | <!-- | ||||
Compile from source, publish binaries, and build & deploy site | 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 --> | <!-- Cleanup --> | ||||
<delete dir="${project.build.dir}" /> | <delete dir="${project.build.dir}" /> | ||||
<delete dir="${project.war.dir}" /> | <delete dir="${project.war.dir}" /> | ||||
summary="Gitblit Manager v${gb.version} (Swing tool to remotely administer a Gitblit server)" | summary="Gitblit Manager v${gb.version} (Swing tool to remotely administer a Gitblit server)" | ||||
labels="Featured, Type-Package, OpSys-All" /> | 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 --> | <!-- Upload Gitblit API Library --> | ||||
<gcupload | <gcupload | ||||
username="${googlecode.user}" | username="${googlecode.user}" |
**New:** *git.garbageCollectionHour = 0* | **New:** *git.garbageCollectionHour = 0* | ||||
**New:** *git.defaultGarbageCollectionThreshold = 500k* | **New:** *git.defaultGarbageCollectionThreshold = 500k* | ||||
**New:** *git.defaultGarbageCollectionPeriod = 7 days* | **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. | 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.requireClientCertificates = false* | ||||
**New:** *git.enforceCertificateValidity = true* | **New:** *git.enforceCertificateValidity = true* | ||||
- Added support for Gitblit GO to require usage of client certificates to access the entire server. | - 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. | 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* | **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 | - Added setting to control length of shortened commit ids | ||||
**New:** *web.shortCommitIdLength=8* | **New:** *web.shortCommitIdLength=8* | ||||
- Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. | - Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. |
/* | |||||
* 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 | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
}); | |||||
} | |||||
} |
public void initialize() { | public void initialize() { | ||||
setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); | 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()); | setContentPane(getUI()); | ||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||||
addWindowListener(new WindowAdapter() { | addWindowListener(new WindowAdapter() { |
/* | |||||
* 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(); | |||||
} | |||||
} | |||||
} |
/* | |||||
* 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; | |||||
} | |||||
} |
/* | |||||
* 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); | |||||
} |
/* | |||||
* 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); | |||||
} | |||||
} |
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); | |||||
} | |||||
} |
/* | |||||
* 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(); | |||||
} | |||||
} |
import javax.swing.SwingConstants; | import javax.swing.SwingConstants; | ||||
import javax.swing.table.DefaultTableCellRenderer; | import javax.swing.table.DefaultTableCellRenderer; | ||||
import com.gitblit.utils.TimeUtils; | |||||
/** | /** | ||||
* Time ago cell renderer with real date tooltip. | * Time ago cell renderer with real date tooltip. | ||||
* | * | ||||
title = "--"; | title = "--"; | ||||
dateString = "never"; | dateString = "never"; | ||||
} else { | } 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); | dateString = new SimpleDateFormat(pattern).format((Date) value); | ||||
} | } | ||||
* @return md5 of the string | * @return md5 of the string | ||||
*/ | */ | ||||
public static String getMD5(String 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 { | try { | ||||
MessageDigest md = MessageDigest.getInstance("MD5"); | MessageDigest md = MessageDigest.getInstance("MD5"); | ||||
md.reset(); | md.reset(); | ||||
md.update(string.getBytes("iso-8859-1")); | |||||
md.update(bytes); | |||||
byte[] digest = md.digest(); | byte[] digest = md.digest(); | ||||
return toHex(digest); | return toHex(digest); | ||||
} catch (UnsupportedEncodingException u) { | |||||
throw new RuntimeException(u); | |||||
} catch (NoSuchAlgorithmException t) { | } catch (NoSuchAlgorithmException t) { | ||||
throw new RuntimeException(t); | throw new RuntimeException(t); | ||||
} | } |
} | } | ||||
} | } | ||||
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) { | private String translate(String key, String defaultValue) { | ||||
String value = defaultValue; | String value = defaultValue; | ||||
if (translation != null && translation.containsKey(key)) { | if (translation != null && translation.containsKey(key)) { |
gb.mutable = mutable | gb.mutable = mutable | ||||
gb.specified = specified | gb.specified = specified | ||||
gb.effective = effective | 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 |