From f66e89662c091e082bd1d2feb6ac91513ccff273 Mon Sep 17 00:00:00 2001 From: Rafael Cavazin <rafaelcavazin@gmail.com> Date: Sun, 21 Jul 2013 09:59:00 -0400 Subject: [PATCH] Merge branch 'master' of https://github.com/gitblit/gitblit --- src/main/java/com/gitblit/authority/GitblitAuthority.java | 923 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 923 insertions(+), 0 deletions(-) diff --git a/src/main/java/com/gitblit/authority/GitblitAuthority.java b/src/main/java/com/gitblit/authority/GitblitAuthority.java new file mode 100644 index 0000000..e0b079e --- /dev/null +++ b/src/main/java/com/gitblit/authority/GitblitAuthority.java @@ -0,0 +1,923 @@ +/* + * 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.Container; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedInputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.URI; +import java.security.PrivateKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +import javax.mail.Message; +import javax.mail.Multipart; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMultipart; +import javax.swing.ImageIcon; +import javax.swing.InputVerifier; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JToolBar; +import javax.swing.RowFilter; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.TableRowSorter; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.StoredConfig; +import org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; +import org.slf4j.LoggerFactory; + +import com.gitblit.ConfigUserService; +import com.gitblit.Constants; +import com.gitblit.FileSettings; +import com.gitblit.IStoredSettings; +import com.gitblit.IUserService; +import com.gitblit.Keys; +import com.gitblit.MailExecutor; +import com.gitblit.client.HeaderPanel; +import com.gitblit.client.Translation; +import com.gitblit.models.UserModel; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.FileUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.TimeUtils; +import com.gitblit.utils.X509Utils; +import com.gitblit.utils.X509Utils.RevocationReason; +import com.gitblit.utils.X509Utils.X509Log; +import com.gitblit.utils.X509Utils.X509Metadata; + +/** + * Simple GUI tool for administering Gitblit client certificates. + * + * @author James Moger + * + */ +public class GitblitAuthority extends JFrame implements X509Log { + + private static final long serialVersionUID = 1L; + + private final UserCertificateTableModel tableModel; + + private UserCertificatePanel userCertificatePanel; + + private File folder; + + private IStoredSettings gitblitSettings; + + private IUserService userService; + + private String caKeystorePassword; + + private JTable table; + + private int defaultDuration; + + private TableRowSorter<UserCertificateTableModel> defaultSorter; + + private MailExecutor mail; + + private JButton certificateDefaultsButton; + + private JButton newSSLCertificate; + + public static void main(String... args) { + // filter out the baseFolder parameter + String folder = "data"; + for (int i = 0; i< args.length; i++) { + String arg = args[i]; + if (arg.equals("--baseFolder")) { + if (i + 1 == args.length) { + System.out.println("Invalid --baseFolder parameter!"); + System.exit(-1); + } else if (args[i + 1] != ".") { + folder = args[i+1]; + } + break; + } + } + final String baseFolder = folder; + EventQueue.invokeLater(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + } + GitblitAuthority authority = new GitblitAuthority(); + authority.initialize(baseFolder); + authority.setLocationRelativeTo(null); + authority.setVisible(true); + } + }); + } + + public GitblitAuthority() { + super(); + tableModel = new UserCertificateTableModel(); + defaultSorter = new TableRowSorter<UserCertificateTableModel>(tableModel); + } + + public void initialize(String baseFolder) { + setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); + setTitle("Gitblit Certificate Authority v" + Constants.getVersion() + " (" + Constants.getBuildDate() + ")"); + setContentPane(getUI()); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent event) { + saveSizeAndPosition(); + } + + @Override + public void windowOpened(WindowEvent event) { + } + }); + + File folder = new File(baseFolder).getAbsoluteFile(); + load(folder); + + setSizeAndPosition(); + } + + private void setSizeAndPosition() { + String sz = null; + String pos = null; + try { + StoredConfig config = getConfig(); + sz = config.getString("ui", null, "size"); + pos = config.getString("ui", null, "position"); + defaultDuration = config.getInt("new", "duration", 365); + } catch (Throwable t) { + t.printStackTrace(); + } + + // try to restore saved window size + if (StringUtils.isEmpty(sz)) { + setSize(900, 600); + } else { + String[] chunks = sz.split("x"); + int width = Integer.parseInt(chunks[0]); + int height = Integer.parseInt(chunks[1]); + setSize(width, height); + } + + // try to restore saved window position + if (StringUtils.isEmpty(pos)) { + setLocationRelativeTo(null); + } else { + String[] chunks = pos.split(","); + int x = Integer.parseInt(chunks[0]); + int y = Integer.parseInt(chunks[1]); + setLocation(x, y); + } + } + + private void saveSizeAndPosition() { + try { + // save window size and position + StoredConfig config = getConfig(); + Dimension sz = GitblitAuthority.this.getSize(); + config.setString("ui", null, "size", + MessageFormat.format("{0,number,0}x{1,number,0}", sz.width, sz.height)); + Point pos = GitblitAuthority.this.getLocationOnScreen(); + config.setString("ui", null, "position", + MessageFormat.format("{0,number,0},{1,number,0}", pos.x, pos.y)); + config.save(); + } catch (Throwable t) { + Utils.showException(GitblitAuthority.this, t); + } + } + + private StoredConfig getConfig() throws IOException, ConfigInvalidException { + File configFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(configFile, FS.detect()); + config.load(); + return config; + } + + private IUserService loadUsers(File folder) { + File file = new File(folder, "gitblit.properties"); + if (!file.exists()) { + return null; + } + gitblitSettings = new FileSettings(file.getAbsolutePath()); + mail = new MailExecutor(gitblitSettings); + String us = gitblitSettings.getString(Keys.realm.userService, "${baseFolder}/users.conf"); + String ext = us.substring(us.lastIndexOf(".") + 1).toLowerCase(); + IUserService service = null; + if (!ext.equals("conf") && !ext.equals("properties")) { + if (us.equals("com.gitblit.LdapUserService")) { + us = gitblitSettings.getString(Keys.realm.ldap.backingUserService, "${baseFolder}/users.conf"); + } else if (us.equals("com.gitblit.LdapUserService")) { + us = gitblitSettings.getString(Keys.realm.redmine.backingUserService, "${baseFolder}/users.conf"); + } + } + + if (us.endsWith(".conf")) { + service = new ConfigUserService(FileUtils.resolveParameter(Constants.baseFolder$, folder, us)); + } else { + throw new RuntimeException("Unsupported user service: " + us); + } + + service = new ConfigUserService(FileUtils.resolveParameter(Constants.baseFolder$, folder, us)); + return service; + } + + private void load(File folder) { + this.folder = folder; + this.userService = loadUsers(folder); + System.out.println(Constants.baseFolder$ + " set to " + folder); + if (userService == null) { + JOptionPane.showMessageDialog(this, MessageFormat.format("Sorry, {0} doesn't look like a Gitblit GO installation.", folder)); + } else { + // build empty certificate model for all users + Map<String, UserCertificateModel> map = new HashMap<String, UserCertificateModel>(); + for (String user : userService.getAllUsernames()) { + UserModel model = userService.getUserModel(user); + UserCertificateModel ucm = new UserCertificateModel(model); + map.put(user, ucm); + } + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + if (certificatesConfigFile.exists()) { + try { + config.load(); + // replace user certificate model with actual data + List<UserCertificateModel> list = UserCertificateConfig.KEY.parse(config).list; + for (UserCertificateModel ucm : list) { + ucm.user = userService.getUserModel(ucm.user.username); + map.put(ucm.user.username, ucm); + } + } catch (IOException e) { + e.printStackTrace(); + } catch (ConfigInvalidException e) { + e.printStackTrace(); + } + } + + tableModel.list = new ArrayList<UserCertificateModel>(map.values()); + Collections.sort(tableModel.list); + tableModel.fireTableDataChanged(); + Utils.packColumns(table, Utils.MARGIN); + + File caKeystore = new File(folder, X509Utils.CA_KEY_STORE); + if (!caKeystore.exists()) { + + if (!X509Utils.unlimitedStrength) { + // prompt to confirm user understands JCE Standard Strength encryption + int res = JOptionPane.showConfirmDialog(GitblitAuthority.this, Translation.get("gb.jceWarning"), + Translation.get("gb.warning"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + if (res != JOptionPane.YES_OPTION) { + if (Desktop.isDesktopSupported()) { + if (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + try { + Desktop.getDesktop().browse(URI.create("http://www.oracle.com/technetwork/java/javase/downloads/index.html")); + } catch (IOException e) { + } + } + } + System.exit(1); + } + } + + // show certificate defaults dialog + certificateDefaultsButton.doClick(); + + // create "localhost" ssl certificate + prepareX509Infrastructure(); + } + } + } + + private boolean prepareX509Infrastructure() { + if (caKeystorePassword == null) { + JPasswordField pass = new JPasswordField(10); + pass.setText(caKeystorePassword); + pass.addAncestorListener(new RequestFocusListener()); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(new JLabel(Translation.get("gb.enterKeystorePassword")), BorderLayout.NORTH); + panel.add(pass, BorderLayout.CENTER); + int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, panel, Translation.get("gb.password"), JOptionPane.OK_CANCEL_OPTION); + if (result == JOptionPane.OK_OPTION) { + caKeystorePassword = new String(pass.getPassword()); + } else { + return false; + } + } + + X509Metadata metadata = new X509Metadata("localhost", caKeystorePassword); + setMetadataDefaults(metadata); + metadata.notAfter = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR); + X509Utils.prepareX509Infrastructure(metadata, folder, this); + return true; + } + + private List<X509Certificate> findCerts(File folder, String username) { + List<X509Certificate> list = new ArrayList<X509Certificate>(); + File userFolder = new File(folder, X509Utils.CERTS + File.separator + username); + if (!userFolder.exists()) { + return list; + } + File [] certs = userFolder.listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.toLowerCase().endsWith(".cer") || name.toLowerCase().endsWith(".crt"); + } + }); + try { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + for (File cert : certs) { + BufferedInputStream is = new BufferedInputStream(new FileInputStream(cert)); + X509Certificate x509 = (X509Certificate) factory.generateCertificate(is); + is.close(); + list.add(x509); + } + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + return list; + } + + private Container getUI() { + userCertificatePanel = new UserCertificatePanel(this) { + + private static final long serialVersionUID = 1L; + @Override + public Insets getInsets() { + return Utils.INSETS; + } + + @Override + public boolean isAllowEmail() { + return mail.isReady(); + } + + @Override + public Date getDefaultExpiration() { + Calendar c = Calendar.getInstance(); + c.add(Calendar.DATE, defaultDuration); + c.set(Calendar.HOUR_OF_DAY, 0); + c.set(Calendar.MINUTE, 0); + c.set(Calendar.SECOND, 0); + c.set(Calendar.MILLISECOND, 0); + return c.getTime(); + } + + @Override + public boolean saveUser(String username, UserCertificateModel ucm) { + return userService.updateUserModel(username, ucm.user); + } + + @Override + public boolean newCertificate(UserCertificateModel ucm, X509Metadata metadata, boolean sendEmail) { + if (!prepareX509Infrastructure()) { + return false; + } + + Date notAfter = metadata.notAfter; + setMetadataDefaults(metadata); + metadata.notAfter = notAfter; + + // set user's specified OID values + UserModel user = ucm.user; + if (!StringUtils.isEmpty(user.organizationalUnit)) { + metadata.oids.put("OU", user.organizationalUnit); + } + if (!StringUtils.isEmpty(user.organization)) { + metadata.oids.put("O", user.organization); + } + if (!StringUtils.isEmpty(user.locality)) { + metadata.oids.put("L", user.locality); + } + if (!StringUtils.isEmpty(user.stateProvince)) { + metadata.oids.put("ST", user.stateProvince); + } + if (!StringUtils.isEmpty(user.countryCode)) { + metadata.oids.put("C", user.countryCode); + } + + File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE); + File zip = X509Utils.newClientBundle(metadata, caKeystoreFile, caKeystorePassword, GitblitAuthority.this); + + // save latest expiration date + if (ucm.expires == null || metadata.notAfter.before(ucm.expires)) { + ucm.expires = metadata.notAfter; + } + + updateAuthorityConfig(ucm); + + // refresh user + ucm.certs = null; + int selectedIndex = table.getSelectedRow(); + tableModel.fireTableDataChanged(); + table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex); + + if (sendEmail) { + sendEmail(user, metadata, zip); + } + return true; + } + + @Override + public boolean revoke(UserCertificateModel ucm, X509Certificate cert, RevocationReason reason) { + if (!prepareX509Infrastructure()) { + return false; + } + + File caRevocationList = new File(folder, X509Utils.CA_REVOCATION_LIST); + File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE); + if (X509Utils.revoke(cert, reason, caRevocationList, caKeystoreFile, caKeystorePassword, GitblitAuthority.this)) { + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + if (certificatesConfigFile.exists()) { + try { + config.load(); + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + } + // add serial to revoked list + ucm.revoke(cert.getSerialNumber(), reason); + ucm.update(config); + try { + config.save(); + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + + // refresh user + ucm.certs = null; + int modelIndex = table.convertRowIndexToModel(table.getSelectedRow()); + tableModel.fireTableDataChanged(); + table.getSelectionModel().setSelectionInterval(modelIndex, modelIndex); + + return true; + } + + return false; + } + }; + + table = Utils.newTable(tableModel, Utils.DATE_FORMAT); + table.setRowSorter(defaultSorter); + table.setDefaultRenderer(CertificateStatus.class, new CertificateStatusRenderer()); + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + int row = table.getSelectedRow(); + if (row < 0) { + return; + } + int modelIndex = table.convertRowIndexToModel(row); + UserCertificateModel ucm = tableModel.get(modelIndex); + if (ucm.certs == null) { + ucm.certs = findCerts(folder, ucm.user.username); + } + userCertificatePanel.setUserCertificateModel(ucm); + } + }); + + JPanel usersPanel = new JPanel(new BorderLayout()) { + + private static final long serialVersionUID = 1L; + + @Override + public Insets getInsets() { + return Utils.INSETS; + } + }; + usersPanel.add(new HeaderPanel(Translation.get("gb.users"), "users_16x16.png"), BorderLayout.NORTH); + usersPanel.add(new JScrollPane(table), BorderLayout.CENTER); + usersPanel.setMinimumSize(new Dimension(400, 10)); + + certificateDefaultsButton = new JButton(new ImageIcon(getClass().getResource("/settings_16x16.png"))); + certificateDefaultsButton.setFocusable(false); + certificateDefaultsButton.setToolTipText(Translation.get("gb.newCertificateDefaults")); + certificateDefaultsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + X509Metadata metadata = new X509Metadata("whocares", "whocares"); + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + NewCertificateConfig certificateConfig = null; + if (certificatesConfigFile.exists()) { + try { + config.load(); + } catch (Exception x) { + Utils.showException(GitblitAuthority.this, x); + } + certificateConfig = NewCertificateConfig.KEY.parse(config); + certificateConfig.update(metadata); + } + InputVerifier verifier = new InputVerifier() { + public boolean verify(JComponent comp) { + boolean returnValue; + JTextField textField = (JTextField) comp; + try { + Integer.parseInt(textField.getText()); + returnValue = true; + } catch (NumberFormatException e) { + returnValue = false; + } + return returnValue; + } + }; + + JTextField siteNameTF = new JTextField(20); + siteNameTF.setText(gitblitSettings.getString(Keys.web.siteName, "Gitblit")); + JPanel siteNamePanel = Utils.newFieldPanel(Translation.get("gb.siteName"), + siteNameTF, Translation.get("gb.siteNameDescription")); + + JTextField validityTF = new JTextField(4); + validityTF.setInputVerifier(verifier); + validityTF.setVerifyInputWhenFocusTarget(true); + validityTF.setText("" + certificateConfig.duration); + JPanel validityPanel = Utils.newFieldPanel(Translation.get("gb.validity"), + validityTF, Translation.get("gb.duration.days").replace("{0}", "").trim()); + + JPanel p1 = new JPanel(new GridLayout(0, 1, 5, 2)); + p1.add(siteNamePanel); + p1.add(validityPanel); + + DefaultOidsPanel oids = new DefaultOidsPanel(metadata); + + JPanel panel = new JPanel(new BorderLayout()); + panel.add(p1, BorderLayout.NORTH); + panel.add(oids, BorderLayout.CENTER); + + int result = JOptionPane.showConfirmDialog(GitblitAuthority.this, + panel, Translation.get("gb.newCertificateDefaults"), JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, new ImageIcon(getClass().getResource("/settings_32x32.png"))); + if (result == JOptionPane.OK_OPTION) { + try { + oids.update(metadata); + certificateConfig.duration = Integer.parseInt(validityTF.getText()); + certificateConfig.store(config, metadata); + config.save(); + + Map<String, String> updates = new HashMap<String, String>(); + updates.put(Keys.web.siteName, siteNameTF.getText()); + gitblitSettings.saveSettings(updates); + } catch (Exception e1) { + Utils.showException(GitblitAuthority.this, e1); + } + } + } + }); + + newSSLCertificate = new JButton(new ImageIcon(getClass().getResource("/rosette_16x16.png"))); + newSSLCertificate.setFocusable(false); + newSSLCertificate.setToolTipText(Translation.get("gb.newSSLCertificate")); + newSSLCertificate.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Date defaultExpiration = new Date(System.currentTimeMillis() + 10*TimeUtils.ONEYEAR); + NewSSLCertificateDialog dialog = new NewSSLCertificateDialog(GitblitAuthority.this, defaultExpiration); + dialog.setModal(true); + dialog.setVisible(true); + if (dialog.isCanceled()) { + return; + } + final Date expires = dialog.getExpiration(); + final String hostname = dialog.getHostname(); + final boolean serveCertificate = dialog.isServeCertificate(); + + AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) { + + @Override + protected Boolean doRequest() throws IOException { + if (!prepareX509Infrastructure()) { + return false; + } + + // read CA private key and certificate + File caKeystoreFile = new File(folder, X509Utils.CA_KEY_STORE); + PrivateKey caPrivateKey = X509Utils.getPrivateKey(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword); + X509Certificate caCert = X509Utils.getCertificate(X509Utils.CA_ALIAS, caKeystoreFile, caKeystorePassword); + + // generate new SSL certificate + X509Metadata metadata = new X509Metadata(hostname, caKeystorePassword); + setMetadataDefaults(metadata); + metadata.notAfter = expires; + File serverKeystoreFile = new File(folder, X509Utils.SERVER_KEY_STORE); + X509Certificate cert = X509Utils.newSSLCertificate(metadata, caPrivateKey, caCert, serverKeystoreFile, GitblitAuthority.this); + boolean hasCert = cert != null; + if (hasCert && serveCertificate) { + // update Gitblit https connector alias + Map<String, String> updates = new HashMap<String, String>(); + updates.put(Keys.server.certificateAlias, metadata.commonName); + gitblitSettings.saveSettings(updates); + } + return hasCert; + } + + @Override + protected void onSuccess() { + if (serveCertificate) { + JOptionPane.showMessageDialog(GitblitAuthority.this, + MessageFormat.format(Translation.get("gb.sslCertificateGeneratedRestart"), hostname), + Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(GitblitAuthority.this, + MessageFormat.format(Translation.get("gb.sslCertificateGenerated"), hostname), + Translation.get("gb.newSSLCertificate"), JOptionPane.INFORMATION_MESSAGE); + } + } + }; + + worker.execute(); + } + }); + + JButton emailBundle = new JButton(new ImageIcon(getClass().getResource("/mail_16x16.png"))); + emailBundle.setFocusable(false); + emailBundle.setToolTipText(Translation.get("gb.emailCertificateBundle")); + emailBundle.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int row = table.getSelectedRow(); + if (row < 0) { + return; + } + int modelIndex = table.convertRowIndexToModel(row); + final UserCertificateModel ucm = tableModel.get(modelIndex); + if (ArrayUtils.isEmpty(ucm.certs)) { + JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.pleaseGenerateClientCertificate"), ucm.user.getDisplayName())); + } + final File zip = new File(folder, X509Utils.CERTS + File.separator + ucm.user.username + File.separator + ucm.user.username + ".zip"); + if (!zip.exists()) { + return; + } + + AuthorityWorker worker = new AuthorityWorker(GitblitAuthority.this) { + @Override + protected Boolean doRequest() throws IOException { + X509Metadata metadata = new X509Metadata(ucm.user.username, "whocares"); + metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME); + if (StringUtils.isEmpty(metadata.serverHostname)) { + metadata.serverHostname = Constants.NAME; + } + metadata.userDisplayname = ucm.user.getDisplayName(); + return sendEmail(ucm.user, metadata, zip); + } + + @Override + protected void onSuccess() { + JOptionPane.showMessageDialog(GitblitAuthority.this, MessageFormat.format(Translation.get("gb.clientCertificateBundleSent"), + ucm.user.getDisplayName())); + } + + }; + worker.execute(); + } + }); + + JButton logButton = new JButton(new ImageIcon(getClass().getResource("/script_16x16.png"))); + logButton.setFocusable(false); + logButton.setToolTipText(Translation.get("gb.log")); + logButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + File log = new File(folder, X509Utils.CERTS + File.separator + "log.txt"); + if (log.exists()) { + String content = FileUtils.readContent(log, "\n"); + JTextArea textarea = new JTextArea(content); + JScrollPane scrollPane = new JScrollPane(textarea); + scrollPane.setPreferredSize(new Dimension(700, 400)); + JOptionPane.showMessageDialog(GitblitAuthority.this, scrollPane, log.getAbsolutePath(), JOptionPane.INFORMATION_MESSAGE); + } + } + }); + + final JTextField filterTextfield = new JTextField(15); + filterTextfield.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + filterUsers(filterTextfield.getText()); + } + }); + filterTextfield.addKeyListener(new KeyAdapter() { + public void keyReleased(KeyEvent e) { + filterUsers(filterTextfield.getText()); + } + }); + + JToolBar buttonControls = new JToolBar(JToolBar.HORIZONTAL); + buttonControls.setFloatable(false); + buttonControls.add(certificateDefaultsButton); + buttonControls.add(newSSLCertificate); + buttonControls.add(emailBundle); + buttonControls.add(logButton); + + JPanel userControls = new JPanel(new FlowLayout(FlowLayout.RIGHT, Utils.MARGIN, Utils.MARGIN)); + userControls.add(new JLabel(Translation.get("gb.filter"))); + userControls.add(filterTextfield); + + JPanel topPanel = new JPanel(new BorderLayout(0, 0)); + topPanel.add(buttonControls, BorderLayout.WEST); + topPanel.add(userControls, BorderLayout.EAST); + + JPanel leftPanel = new JPanel(new BorderLayout()); + leftPanel.add(topPanel, BorderLayout.NORTH); + leftPanel.add(usersPanel, BorderLayout.CENTER); + + userCertificatePanel.setMinimumSize(new Dimension(375, 10)); + + JLabel statusLabel = new JLabel(); + statusLabel.setHorizontalAlignment(SwingConstants.RIGHT); + if (X509Utils.unlimitedStrength) { + statusLabel.setText("JCE Unlimited Strength Jurisdiction Policy"); + } else { + statusLabel.setText("JCE Standard Encryption Policy"); + } + + JPanel root = new JPanel(new BorderLayout()) { + private static final long serialVersionUID = 1L; + public Insets getInsets() { + return Utils.INSETS; + } + }; + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, userCertificatePanel); + splitPane.setDividerLocation(1d); + root.add(splitPane, BorderLayout.CENTER); + root.add(statusLabel, BorderLayout.SOUTH); + return root; + } + + private void filterUsers(final String fragment) { + table.clearSelection(); + userCertificatePanel.setUserCertificateModel(null); + if (StringUtils.isEmpty(fragment)) { + table.setRowSorter(defaultSorter); + return; + } + RowFilter<UserCertificateTableModel, Object> containsFilter = new RowFilter<UserCertificateTableModel, Object>() { + public boolean include(Entry<? extends UserCertificateTableModel, ? extends Object> entry) { + for (int i = entry.getValueCount() - 1; i >= 0; i--) { + if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) { + return true; + } + } + return false; + } + }; + TableRowSorter<UserCertificateTableModel> sorter = new TableRowSorter<UserCertificateTableModel>( + tableModel); + sorter.setRowFilter(containsFilter); + table.setRowSorter(sorter); + } + + @Override + public void log(String message) { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(new File(folder, X509Utils.CERTS + File.separator + "log.txt"), true)); + writer.write(MessageFormat.format("{0,date,yyyy-MM-dd HH:mm}: {1}", new Date(), message)); + writer.newLine(); + writer.flush(); + } catch (Exception e) { + LoggerFactory.getLogger(GitblitAuthority.class).error("Failed to append log entry!", e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + } + } + } + } + + private boolean sendEmail(UserModel user, X509Metadata metadata, File zip) { + // send email + try { + if (mail.isReady()) { + Message message = mail.createMessage(user.emailAddress); + message.setSubject("Your Gitblit client certificate for " + metadata.serverHostname); + + // body of email + String body = X509Utils.processTemplate(new File(folder, X509Utils.CERTS + File.separator + "mail.tmpl"), metadata); + if (StringUtils.isEmpty(body)) { + body = MessageFormat.format("Hi {0}\n\nHere is your client certificate bundle.\nInside the zip file are installation instructions.", user.getDisplayName()); + } + Multipart mp = new MimeMultipart(); + MimeBodyPart messagePart = new MimeBodyPart(); + messagePart.setText(body); + mp.addBodyPart(messagePart); + + // attach zip + MimeBodyPart filePart = new MimeBodyPart(); + FileDataSource fds = new FileDataSource(zip); + filePart.setDataHandler(new DataHandler(fds)); + filePart.setFileName(fds.getName()); + mp.addBodyPart(filePart); + + message.setContent(mp); + + mail.sendNow(message); + return true; + } else { + JOptionPane.showMessageDialog(GitblitAuthority.this, "Sorry, the mail server settings are not configured properly.\nCan not send email.", Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE); + } + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + return false; + } + + private void setMetadataDefaults(X509Metadata metadata) { + metadata.serverHostname = gitblitSettings.getString(Keys.web.siteName, Constants.NAME); + if (StringUtils.isEmpty(metadata.serverHostname)) { + metadata.serverHostname = Constants.NAME; + } + + // set default values from config file + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + if (certificatesConfigFile.exists()) { + try { + config.load(); + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config); + certificateConfig.update(metadata); + } + } + + private void updateAuthorityConfig(UserCertificateModel ucm) { + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + if (certificatesConfigFile.exists()) { + try { + config.load(); + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + } + ucm.update(config); + try { + config.save(); + } catch (Exception e) { + Utils.showException(GitblitAuthority.this, e); + } + } +} -- Gitblit v1.9.1