summaryrefslogtreecommitdiffstats
path: root/src/com/gitblit
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2012-11-23 12:01:47 -0500
committerJames Moger <james.moger@gitblit.com>2012-11-23 12:01:47 -0500
commit4ad1ebce249519369d24b4ea7da1da62a685627a (patch)
tree765afbc2bf5cb374bb731ad6169b591119c2def9 /src/com/gitblit
parent0040210c8290bf60b8b08437d18b6cc05e863f32 (diff)
downloadgitblit-4ad1ebce249519369d24b4ea7da1da62a685627a.tar.gz
gitblit-4ad1ebce249519369d24b4ea7da1da62a685627a.zip
Mostly functional Gitblit Certificate Authority tool
Diffstat (limited to 'src/com/gitblit')
-rw-r--r--src/com/gitblit/authority/CertificateStatus.java20
-rw-r--r--src/com/gitblit/authority/CertificateStatusRenderer.java82
-rw-r--r--src/com/gitblit/authority/CertificatesTableModel.java166
-rw-r--r--src/com/gitblit/authority/GitblitAuthority.java2
-rw-r--r--src/com/gitblit/authority/GitblitAuthorityLauncher.java113
-rw-r--r--src/com/gitblit/authority/NewClientCertificateDialog.java164
-rw-r--r--src/com/gitblit/authority/UserCertificatePanel.java334
-rw-r--r--src/com/gitblit/authority/UserCertificateTableModel.java131
-rw-r--r--src/com/gitblit/authority/Utils.java101
-rw-r--r--src/com/gitblit/authority/X509CertificateViewer.java129
-rw-r--r--src/com/gitblit/client/DateCellRenderer.java10
-rw-r--r--src/com/gitblit/utils/StringUtils.java18
-rw-r--r--src/com/gitblit/utils/TimeUtils.java16
-rw-r--r--src/com/gitblit/wicket/GitBlitWebApp.properties41
14 files changed, 1320 insertions, 7 deletions
diff --git a/src/com/gitblit/authority/CertificateStatus.java b/src/com/gitblit/authority/CertificateStatus.java
new file mode 100644
index 00000000..79c51628
--- /dev/null
+++ b/src/com/gitblit/authority/CertificateStatus.java
@@ -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
+}
diff --git a/src/com/gitblit/authority/CertificateStatusRenderer.java b/src/com/gitblit/authority/CertificateStatusRenderer.java
new file mode 100644
index 00000000..7a708ea4
--- /dev/null
+++ b/src/com/gitblit/authority/CertificateStatusRenderer.java
@@ -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;
+ }
+}
diff --git a/src/com/gitblit/authority/CertificatesTableModel.java b/src/com/gitblit/authority/CertificatesTableModel.java
new file mode 100644
index 00000000..44d80e3a
--- /dev/null
+++ b/src/com/gitblit/authority/CertificatesTableModel.java
@@ -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;
+ }
+ });
+ }
+}
diff --git a/src/com/gitblit/authority/GitblitAuthority.java b/src/com/gitblit/authority/GitblitAuthority.java
index ff48ecf9..84162000 100644
--- a/src/com/gitblit/authority/GitblitAuthority.java
+++ b/src/com/gitblit/authority/GitblitAuthority.java
@@ -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() {
diff --git a/src/com/gitblit/authority/GitblitAuthorityLauncher.java b/src/com/gitblit/authority/GitblitAuthorityLauncher.java
new file mode 100644
index 00000000..584ac01f
--- /dev/null
+++ b/src/com/gitblit/authority/GitblitAuthorityLauncher.java
@@ -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();
+ }
+ }
+}
diff --git a/src/com/gitblit/authority/NewClientCertificateDialog.java b/src/com/gitblit/authority/NewClientCertificateDialog.java
new file mode 100644
index 00000000..ad4fe9aa
--- /dev/null
+++ b/src/com/gitblit/authority/NewClientCertificateDialog.java
@@ -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;
+ }
+}
diff --git a/src/com/gitblit/authority/UserCertificatePanel.java b/src/com/gitblit/authority/UserCertificatePanel.java
new file mode 100644
index 00000000..20727f64
--- /dev/null
+++ b/src/com/gitblit/authority/UserCertificatePanel.java
@@ -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);
+}
diff --git a/src/com/gitblit/authority/UserCertificateTableModel.java b/src/com/gitblit/authority/UserCertificateTableModel.java
new file mode 100644
index 00000000..dde73fc0
--- /dev/null
+++ b/src/com/gitblit/authority/UserCertificateTableModel.java
@@ -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);
+ }
+}
diff --git a/src/com/gitblit/authority/Utils.java b/src/com/gitblit/authority/Utils.java
new file mode 100644
index 00000000..5c824934
--- /dev/null
+++ b/src/com/gitblit/authority/Utils.java
@@ -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);
+ }
+}
diff --git a/src/com/gitblit/authority/X509CertificateViewer.java b/src/com/gitblit/authority/X509CertificateViewer.java
new file mode 100644
index 00000000..d29c6e7d
--- /dev/null
+++ b/src/com/gitblit/authority/X509CertificateViewer.java
@@ -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();
+ }
+}
diff --git a/src/com/gitblit/client/DateCellRenderer.java b/src/com/gitblit/client/DateCellRenderer.java
index 954dad2a..751c7dbb 100644
--- a/src/com/gitblit/client/DateCellRenderer.java
+++ b/src/com/gitblit/client/DateCellRenderer.java
@@ -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);
}
diff --git a/src/com/gitblit/utils/StringUtils.java b/src/com/gitblit/utils/StringUtils.java
index 325b529f..86840048 100644
--- a/src/com/gitblit/utils/StringUtils.java
+++ b/src/com/gitblit/utils/StringUtils.java
@@ -263,13 +263,25 @@ public class StringUtils {
*/
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);
}
diff --git a/src/com/gitblit/utils/TimeUtils.java b/src/com/gitblit/utils/TimeUtils.java
index 7f695625..ec8871c6 100644
--- a/src/com/gitblit/utils/TimeUtils.java
+++ b/src/com/gitblit/utils/TimeUtils.java
@@ -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)) {
diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties
index 22ae92f7..6b2102ef 100644
--- a/src/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -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 \ No newline at end of file