From e8c417f4e63f84ac6e14f6d5540dcb1f0f9862fc Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 23 Nov 2012 10:46:35 -0500 Subject: [PATCH] Command-line tool to generate client certificate bundles for existing users --- build.xml | 2 + distrib/instructions.tmpl | 123 ++++++++++ distrib/mail.tmpl | 7 + distrib/makeclientcertificate.cmd | 1 + distrib/makeclientcertificate.sh | 2 + src/com/gitblit/ConfigUserService.java | 30 +++ src/com/gitblit/MailExecutor.java | 4 + .../authority/MakeClientCertificate.java | 230 ++++++++++++++++++ .../authority/NewCertificateConfig.java | 72 ++++++ .../authority/UserCertificateConfig.java | 69 ++++++ .../authority/UserCertificateModel.java | 134 ++++++++++ src/com/gitblit/models/UserModel.java | 5 + 12 files changed, 679 insertions(+) create mode 100644 distrib/instructions.tmpl create mode 100644 distrib/mail.tmpl create mode 100644 distrib/makeclientcertificate.cmd create mode 100644 distrib/makeclientcertificate.sh create mode 100644 src/com/gitblit/authority/MakeClientCertificate.java create mode 100644 src/com/gitblit/authority/NewCertificateConfig.java create mode 100644 src/com/gitblit/authority/UserCertificateConfig.java create mode 100644 src/com/gitblit/authority/UserCertificateModel.java diff --git a/build.xml b/build.xml index 4e735195..7c5e8cc5 100644 --- a/build.xml +++ b/build.xml @@ -183,6 +183,7 @@ + @@ -195,6 +196,7 @@ + diff --git a/distrib/instructions.tmpl b/distrib/instructions.tmpl new file mode 100644 index 00000000..11ea78ff --- /dev/null +++ b/distrib/instructions.tmpl @@ -0,0 +1,123 @@ +******************************************************************************** + Gitblit SSL Client Certificate for $serverHostname +******************************************************************************** + + Hello $userDisplayname, + + Your private key, public certificate, and the Gitblit Certificate Authority + certificate for $serverHostname are stored in $username.p12, a PKCS#12 certificate + store[1], and also in $username.pem, a PEM certificate store. + + Both of these certificate stores are password-protected. + Password Hint: $storePasswordHint + + +Git (All) Installation Instructions +============================================= + + The provided PEM file can be directly used by your git client. + + git config [--global] http.sslCert path/to/$username.pem + + The supplied PEM file is password-protected and you may be prompted for your + password multiple times during an exchange with Gitblit. If you desire a + password-less git client workflow then you will need to decrypt and export your + private key with OpenSSL[2] and then update your git config to use that key. + + openssl rsa -in path/to/$username.pem -out path/to/$username.key + git config [--global] http.sslKey path/to/$username.key + + Obviously, you should protect access to any decrypted private key. + + NOTE: + Some older git clients may have trouble using the PEM file without explicitly + extracting the private key. This has been observed, for example, on Ubuntu 12.04 + with git 1.7.9.5. + + +Firefox (All) Installation Instructions +============================================= + + Firefox maintains it's own certificate store which is separate from the operating + system. + + 1. Navigate to Options->Advanced->Encryption + 2. Click "View Certificates" + 3. Switch to the "Your Certificates" tab + 4. Click "Import..." + 5. Navigate your filesystem and select $username.p12 + 6. At the password prompt enter the certificate store password + You have now imported your private key, public certificate, and the CA certificate + but now we must manually set the trust settings of the CA certificate. + 7. Switch to the "Authorities" tab + 8. Scroll down and find "Gitblit-> Gitblit Certificate Authority" + 9. Select it and click "Edit Trust..." + 10. Check "This certificate can identify websites" and click OK. + + +Chrome/IE (Windows) Installation Instructions +============================================= + + On Windows, Chrome and IE share their certificate store so configuring one will + automatically apply for both. + + IE + ------------------------------------ + 1. Navigate to Internet Options->Content + 2. Click the "Certificates" button + + Chrome + ------------------------------------ + 1. Navigate to Settings->Show Advanced Settings->HTTP/SSL + 2. Click the "Manage Certificates..." button + + Both (Windows) + ------------------------------------ + 3. Switch to the "Personal" tab + 4. Click the "Import..." button + 5. Follow the Import Wizard instructions. + You will need to change the selected file filter when navigating to $username.p12 + 6. At the password prompt enter the certificate store password + 7. Because both your personal certificate and the CA certifcate are stored in + $username.p12, you must choose "Automatically select the certificate store based on the type of certificate". + If you choose the default you will not install the CA certificate. + + +Chrome (Linux) Installation Instructions +============================================= + + On Linux, Chrome maintains it's own certificate store. + + 1. Navigate to Settings->Show Advanced Settings->HTTP/SSL + 2. Click the "Manage Certificates..." button + 3. Navigate your filesystem and select $username.p12 + 4. At the password prompt enter the certificate store password + You have now imported your private key, public certificate, and the CA certificate + but now we must manually set the trust settings of the CA certificate. + 5. Switch to the "Authorities" tab + 6. Scroll down and find "Gitblit-> Gitblit Certificate Authority" + 7. Select it and click "Edit Trust..." + 8. Check "This certificate can identify websites" and click OK. + + +Chrome/Safari (Mac OS X) Installation Instructions +============================================= + +On Mac OS X, Chrome and Safari both use Keychain Access to store certificates +so configuring one will automatically apply for both. + + 1. Double-click $username.pem + 2. At the password prompt enter the certificate store password + You have now imported your private key, public certificate, and the CA certificate + but now we must manually set the trust settings of the CA certificate. + 3. Find the Gitblit Certificate Authority certificate, it should have a red + indicator meaning untrusted, and double-click it. + 4. Open the "Trust" disclosure triangle and change "When using this certificate" + to "Always Trust". + 5. Close the certificate view and enter your system password to save the changes + to your keychain. + + +[1] PKCS#12 is one of the standard container formats for sharing private keys and + public certificates. +[2] http://www.openssl.org diff --git a/distrib/mail.tmpl b/distrib/mail.tmpl new file mode 100644 index 00000000..463e124d --- /dev/null +++ b/distrib/mail.tmpl @@ -0,0 +1,7 @@ + Hello $userDisplayname, + + Your private key, public certificate, and the Gitblit Certificate Authority + certificate for $serverHostname are bundled together in the attached zip file. + + There are also setup/installation instructions included in the zip for Git and + several major browsers to get you started. \ No newline at end of file diff --git a/distrib/makeclientcertificate.cmd b/distrib/makeclientcertificate.cmd new file mode 100644 index 00000000..72941421 --- /dev/null +++ b/distrib/makeclientcertificate.cmd @@ -0,0 +1 @@ +@java -cp gitblit.jar;"%CD%\ext\*" com.gitblit.authority.MakeClientCertificate diff --git a/distrib/makeclientcertificate.sh b/distrib/makeclientcertificate.sh new file mode 100644 index 00000000..76a195ee --- /dev/null +++ b/distrib/makeclientcertificate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +java -cp gitblit.jar:$PWD/ext/* com.gitblit.authority.MakeClientCertificate diff --git a/src/com/gitblit/ConfigUserService.java b/src/com/gitblit/ConfigUserService.java index 9ad805b6..068bbe3a 100644 --- a/src/com/gitblit/ConfigUserService.java +++ b/src/com/gitblit/ConfigUserService.java @@ -66,6 +66,16 @@ public class ConfigUserService implements IUserService { private static final String EMAILADDRESS = "emailAddress"; + private static final String ORGANIZATIONALUNIT = "organizationalUnit"; + + private static final String ORGANIZATION = "organization"; + + private static final String LOCALITY = "locality"; + + private static final String STATEPROVINCE = "stateProvince"; + + private static final String COUNTRYCODE = "countryCode"; + private static final String COOKIE = "cookie"; private static final String REPOSITORY = "repository"; @@ -817,6 +827,21 @@ public class ConfigUserService implements IUserService { if (!StringUtils.isEmpty(model.emailAddress)) { config.setString(USER, model.username, EMAILADDRESS, model.emailAddress); } + if (!StringUtils.isEmpty(model.organizationalUnit)) { + config.setString(USER, model.username, ORGANIZATIONALUNIT, model.organizationalUnit); + } + if (!StringUtils.isEmpty(model.organization)) { + config.setString(USER, model.username, ORGANIZATION, model.organization); + } + if (!StringUtils.isEmpty(model.locality)) { + config.setString(USER, model.username, LOCALITY, model.locality); + } + if (!StringUtils.isEmpty(model.stateProvince)) { + config.setString(USER, model.username, STATEPROVINCE, model.stateProvince); + } + if (!StringUtils.isEmpty(model.countryCode)) { + config.setString(USER, model.username, COUNTRYCODE, model.countryCode); + } // user roles List roles = new ArrayList(); @@ -964,6 +989,11 @@ public class ConfigUserService implements IUserService { user.password = config.getString(USER, username, PASSWORD); user.displayName = config.getString(USER, username, DISPLAYNAME); user.emailAddress = config.getString(USER, username, EMAILADDRESS); + user.organizationalUnit = config.getString(USER, username, ORGANIZATIONALUNIT); + user.organization = config.getString(USER, username, ORGANIZATION); + user.locality = config.getString(USER, username, LOCALITY); + user.stateProvince = config.getString(USER, username, STATEPROVINCE); + user.countryCode = config.getString(USER, username, COUNTRYCODE); user.cookie = config.getString(USER, username, COOKIE); if (StringUtils.isEmpty(user.cookie) && !StringUtils.isEmpty(user.password)) { user.cookie = StringUtils.getSHA1(user.username + user.password); diff --git a/src/com/gitblit/MailExecutor.java b/src/com/gitblit/MailExecutor.java index ea19edbf..9001e836 100644 --- a/src/com/gitblit/MailExecutor.java +++ b/src/com/gitblit/MailExecutor.java @@ -231,4 +231,8 @@ public class MailExecutor implements Runnable { } } } + + public void sendNow(Message message) throws Exception { + Transport.send(message); + } } diff --git a/src/com/gitblit/authority/MakeClientCertificate.java b/src/com/gitblit/authority/MakeClientCertificate.java new file mode 100644 index 00000000..5829fc11 --- /dev/null +++ b/src/com/gitblit/authority/MakeClientCertificate.java @@ -0,0 +1,230 @@ +/* + * 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.io.File; +import java.text.MessageFormat; +import java.util.Date; + +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 org.eclipse.jgit.storage.file.FileBasedConfig; +import org.eclipse.jgit.util.FS; + +import com.beust.jcommander.JCommander; +import com.beust.jcommander.Parameter; +import com.beust.jcommander.ParameterException; +import com.beust.jcommander.Parameters; +import com.gitblit.ConfigUserService; +import com.gitblit.Constants; +import com.gitblit.FileSettings; +import com.gitblit.IUserService; +import com.gitblit.Keys; +import com.gitblit.MailExecutor; +import com.gitblit.models.UserModel; +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.TimeUtils; +import com.gitblit.utils.X509Utils; +import com.gitblit.utils.X509Utils.X509Metadata; + +/** + * Utility class to generate self-signed certificates. + * + * @author James Moger + * + */ +public class MakeClientCertificate { + + public static void main(String... args) throws Exception { + Params params = new Params(); + JCommander jc = new JCommander(params); + try { + jc.parse(args); + } catch (ParameterException t) { + System.err.println(t.getMessage()); + jc.usage(); + System.exit(-1); + } + + // Load the user list + String us = Params.FILESETTINGS.getString(Keys.realm.userService, "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 = Params.FILESETTINGS.getString(Keys.realm.ldap.backingUserService, "users.conf"); + } else if (us.equals("com.gitblit.LdapUserService")) { + us = Params.FILESETTINGS.getString(Keys.realm.redmine.backingUserService, "users.conf"); + } + } + + if (us.endsWith(".conf")) { + service = new ConfigUserService(new File(us)); + } else { + throw new RuntimeException("Unsupported user service: " + us); + } + + // Confirm the user exists + UserModel user = service.getUserModel(params.username); + if (user == null) { + System.out.println(MessageFormat.format("Failed to find user \"{0}\" in {1}", params.username, us)); + System.exit(-1); + } + + File folder = new File(System.getProperty("user.dir")); + X509Metadata serverMetadata = new X509Metadata("localhost", params.storePassword); + X509Utils.prepareX509Infrastructure(serverMetadata, folder); + + File caStore = new File(folder, X509Utils.CA_KEY_STORE); + + X509Metadata clientMetadata = new X509Metadata(params.username, params.password); + clientMetadata.userDisplayname = user.getDisplayName(); + clientMetadata.emailAddress = user.emailAddress; + clientMetadata.serverHostname = params.serverHostname; + clientMetadata.passwordHint = params.hint; + + UserCertificateModel ucm = null; + + // set default values from config file + File certificatesConfigFile = new File(folder, X509Utils.CA_CONFIG); + FileBasedConfig config = new FileBasedConfig(certificatesConfigFile, FS.detect()); + if (certificatesConfigFile.exists()) { + config.load(); + NewCertificateConfig certificateConfig = NewCertificateConfig.KEY.parse(config); + certificateConfig.update(clientMetadata); + + ucm = UserCertificateConfig.KEY.parse(config).getUserCertificateModel(params.username); + } + + // set user's specified OID values + if (!StringUtils.isEmpty(user.organizationalUnit)) { + clientMetadata.oids.put("OU", user.organizationalUnit); + } + if (!StringUtils.isEmpty(user.organization)) { + clientMetadata.oids.put("O", user.organization); + } + if (!StringUtils.isEmpty(user.locality)) { + clientMetadata.oids.put("L", user.locality); + } + if (!StringUtils.isEmpty(user.stateProvince)) { + clientMetadata.oids.put("ST", user.stateProvince); + } + if (!StringUtils.isEmpty(user.countryCode)) { + clientMetadata.oids.put("C", user.countryCode); + } + + if (params.duration > 0) { + // overriding duration from command-line parameter + clientMetadata.notAfter = new Date(System.currentTimeMillis() + TimeUtils.ONEDAY * params.duration); + } + + // generate zip bundle + File zip = X509Utils.newClientBundle(clientMetadata, caStore, params.storePassword); + + String indent = " "; + System.out.println(MessageFormat.format("Client certificate bundle generated for {0}", params.username)); + System.out.print(indent); + System.out.println(zip); + + // update certificates.conf + if (ucm == null) { + ucm = new UserCertificateModel(new UserModel(params.username)); + } + + // save latest expiration date + if (ucm.expires == null || clientMetadata.notAfter.after(ucm.expires)) { + ucm.expires = clientMetadata.notAfter; + } + ucm.update(config); + config.save(); + + if (params.sendEmail) { + if (StringUtils.isEmpty(user.emailAddress)) { + System.out.print(indent); + System.out.println(MessageFormat.format("User \"{0}\" does not have an email address.", user.username)); + } else { + // send email + MailExecutor mail = new MailExecutor(Params.FILESETTINGS); + if (mail.isReady()) { + Message message = mail.createMessage(user.emailAddress); + message.setSubject("Your Gitblit client certificate for " + clientMetadata.serverHostname); + + // body of email + String body = X509Utils.processTemplate(new File(caStore.getParentFile(), "mail.tmpl"), clientMetadata); + 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); + System.out.println(); + System.out.println("Mail sent."); + } else { + System.out.print(indent); + System.out.println("Mail server is not properly configured. Can not send email."); + } + } + } + } + + /** + * JCommander Parameters class for MakeClientCertificate. + */ + @Parameters(separators = " ") + private static class Params { + + private static final FileSettings FILESETTINGS = new FileSettings(Constants.PROPERTIES_FILE); + + @Parameter(names = { "--username" }, description = "Username for certificate (CN)", required = true) + public String username; + + @Parameter(names = { "--password" }, description = "Password to secure user's certificate (<=7 chars unless JCE Unlimited Strength installed)", required = true) + public String password; + + @Parameter(names = { "--hint" }, description = "Hint for password", required = true) + public String hint; + + @Parameter(names = "--duration", description = "Number of days from now until the certificate expires") + public int duration = 0; + + @Parameter(names = "--storePassword", description = "Password for CA keystore.") + public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, ""); + + @Parameter(names = "--server", description = "Hostname or server identity") + public String serverHostname = Params.FILESETTINGS.getString(Keys.web.siteName, "localhost"); + + @Parameter(names = "--sendEmail", description = "Send an email to the user with their bundle") + public boolean sendEmail; + + } +} diff --git a/src/com/gitblit/authority/NewCertificateConfig.java b/src/com/gitblit/authority/NewCertificateConfig.java new file mode 100644 index 00000000..e4db130e --- /dev/null +++ b/src/com/gitblit/authority/NewCertificateConfig.java @@ -0,0 +1,72 @@ +/* + * 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.Date; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Config.SectionParser; + +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.TimeUtils; +import com.gitblit.utils.X509Utils.X509Metadata; + +/** + * Certificate config file parser. + * + * @author James Moger + */ +public class NewCertificateConfig { + public static final SectionParser KEY = new SectionParser() { + public NewCertificateConfig parse(final Config cfg) { + return new NewCertificateConfig(cfg); + } + }; + + public final String OU; + public final String O; + public final String L; + public final String ST; + public final String C; + + public final int duration; + + private NewCertificateConfig(final Config c) { + duration = c.getInt("new", null, "duration", 0); + OU = c.getString("new", null, "organizationalUnit"); + O = c.getString("new", null, "organization"); + L = c.getString("new", null, "locality"); + ST = c.getString("new", null, "stateProvince"); + C = c.getString("new", null, "countryCode"); + } + + public void update(X509Metadata metadata) { + update(metadata, "OU", OU); + update(metadata, "O", O); + update(metadata, "L", L); + update(metadata, "ST", ST); + update(metadata, "C", C); + if (duration > 0) { + metadata.notAfter = new Date(System.currentTimeMillis() + duration*TimeUtils.ONEDAY); + } + } + + private void update(X509Metadata metadata, String oid, String value) { + if (!StringUtils.isEmpty(value)) { + metadata.oids.put(oid, value); + } + } + } \ No newline at end of file diff --git a/src/com/gitblit/authority/UserCertificateConfig.java b/src/com/gitblit/authority/UserCertificateConfig.java new file mode 100644 index 00000000..47132a06 --- /dev/null +++ b/src/com/gitblit/authority/UserCertificateConfig.java @@ -0,0 +1,69 @@ +/* + * 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.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Config.SectionParser; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants; +import com.gitblit.models.UserModel; + +/** + * User certificate config section parser. + * + * @author James Moger + */ +public class UserCertificateConfig { + public static final SectionParser KEY = new SectionParser() { + public UserCertificateConfig parse(final Config cfg) { + return new UserCertificateConfig(cfg); + } + }; + + public final List list; + + private UserCertificateConfig(final Config c) { + SimpleDateFormat df = new SimpleDateFormat(Constants.ISO8601); + list = new ArrayList(); + for (String username : c.getSubsections("user")) { + UserCertificateModel uc = new UserCertificateModel(new UserModel(username)); + try { + uc.expires = df.parse(c.getString("user", username, "expires")); + } catch (ParseException e) { + LoggerFactory.getLogger(UserCertificateConfig.class).error("Failed to parse date!", e); + } + uc.notes = c.getString("user", username, "notes"); + uc.revoked = new ArrayList(Arrays.asList(c.getStringList("user", username, "revoked"))); + list.add(uc); + } + } + + public UserCertificateModel getUserCertificateModel(String username) { + for (UserCertificateModel ucm : list) { + if (ucm.user.username.equalsIgnoreCase(username)) { + return ucm; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/com/gitblit/authority/UserCertificateModel.java b/src/com/gitblit/authority/UserCertificateModel.java new file mode 100644 index 00000000..f5d71bb0 --- /dev/null +++ b/src/com/gitblit/authority/UserCertificateModel.java @@ -0,0 +1,134 @@ +/* + * 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.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.eclipse.jgit.lib.Config; + +import com.gitblit.Constants; +import com.gitblit.models.UserModel; +import com.gitblit.utils.ArrayUtils; +import com.gitblit.utils.TimeUtils; +import com.gitblit.utils.X509Utils.RevocationReason; + +public class UserCertificateModel implements Comparable { + public UserModel user; + public Date expires; + public List certs; + public List revoked; + public String notes; + + public UserCertificateModel(UserModel user) { + this.user = user; + } + + public void update(Config config) { + if (expires != null) { + SimpleDateFormat df = new SimpleDateFormat(Constants.ISO8601); + config.setString("user", user.username, "expires", df.format(expires)); + } + if (notes != null) { + config.setString("user", user.username, "notes", notes); + } + if (!ArrayUtils.isEmpty(revoked)) { + config.setStringList("user", user.username, "revoked", revoked); + } + } + + @Override + public int compareTo(UserCertificateModel o) { + return user.compareTo(o.user); + } + + public void revoke(BigInteger serial, RevocationReason reason) { + if (revoked == null) { + revoked = new ArrayList(); + } + revoked.add(serial.toString() + ":" + reason.ordinal()); + } + + public boolean isRevoked(BigInteger serial) { + return isRevoked(serial.toString()); + } + + public boolean isRevoked(String serial) { + if (ArrayUtils.isEmpty(revoked)) { + return false; + } + String sn = serial + ":"; + for (String s : revoked) { + if (s.startsWith(sn)) { + return true; + } + } + return false; + } + + public RevocationReason getRevocationReason(BigInteger serial) { + try { + String sn = serial + ":"; + for (String s : revoked) { + if (s.startsWith(sn)) { + String r = s.substring(sn.length()); + int i = Integer.parseInt(r); + return RevocationReason.values()[i]; + } + } + } catch (Exception e) { + } + return RevocationReason.unspecified; + } + + public CertificateStatus getStatus() { + if (expires == null) { + return CertificateStatus.unknown; + } else if (isExpired(expires)) { + return CertificateStatus.expired; + } else if (isExpiring(expires)) { + return CertificateStatus.expiring; + } + return CertificateStatus.ok; + } + + public boolean hasExpired() { + return expires != null && isExpiring(expires); + } + + public CertificateStatus getStatus(X509Certificate cert) { + if (isRevoked(cert.getSerialNumber())) { + return CertificateStatus.revoked; + } else if (isExpired(cert.getNotAfter())) { + return CertificateStatus.expired; + } else if (isExpiring(cert.getNotAfter())) { + return CertificateStatus.expiring; + } + return CertificateStatus.ok; + } + + private boolean isExpiring(Date date) { + return (date.getTime() - System.currentTimeMillis()) <= TimeUtils.ONEDAY * 30; + } + + private boolean isExpired(Date date) { + return date.getTime() < System.currentTimeMillis(); + } + } \ No newline at end of file diff --git a/src/com/gitblit/models/UserModel.java b/src/com/gitblit/models/UserModel.java index 1159905d..bd40985f 100644 --- a/src/com/gitblit/models/UserModel.java +++ b/src/com/gitblit/models/UserModel.java @@ -56,6 +56,11 @@ public class UserModel implements Principal, Serializable, Comparable public String cookie; public String displayName; public String emailAddress; + public String organizationalUnit; + public String organization; + public String locality; + public String stateProvince; + public String countryCode; public boolean canAdmin; public boolean canFork; public boolean canCreate; -- 2.39.5