From 05f229883c4e15e044c5c103acf69265cfb8806e Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Thu, 05 Jun 2014 08:38:35 -0400 Subject: [PATCH] Add a basic SSH public key management UI --- src/main/java/com/gitblit/manager/IRuntimeManager.java | 27 +++ src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java | 161 ++++++++++++++++++++ src/main/java/com/gitblit/wicket/pages/UserPage.java | 22 ++ src/main/java/com/gitblit/GitBlit.java | 15 + src/main/java/com/gitblit/wicket/GitBlitWebApp.properties | 10 + src/main/java/com/gitblit/wicket/panels/TextAreaOption.java | 54 ++++++ src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html | 46 +++++ src/main/java/com/gitblit/manager/RuntimeManager.java | 39 ++++ src/main/java/com/gitblit/wicket/pages/UserPage.html | 14 + src/main/java/com/gitblit/manager/GitblitManager.java | 15 + src/main/java/com/gitblit/manager/ServicesManager.java | 18 + src/main/java/com/gitblit/wicket/panels/TextAreaOption.html | 20 ++ src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java | 15 + 13 files changed, 449 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java index 3db5f08..8179385 100644 --- a/src/main/java/com/gitblit/GitBlit.java +++ b/src/main/java/com/gitblit/GitBlit.java @@ -117,6 +117,21 @@ return servicesManager.isServingRepositories(); } + @Override + public boolean isServingHTTP() { + return servicesManager.isServingHTTP(); + } + + @Override + public boolean isServingGIT() { + return servicesManager.isServingGIT(); + } + + @Override + public boolean isServingSSH() { + return servicesManager.isServingSSH(); + } + protected Object [] getModules() { return new Object [] { new GitBlitModule()}; } diff --git a/src/main/java/com/gitblit/manager/GitblitManager.java b/src/main/java/com/gitblit/manager/GitblitManager.java index 16c71ba..ef2433d 100644 --- a/src/main/java/com/gitblit/manager/GitblitManager.java +++ b/src/main/java/com/gitblit/manager/GitblitManager.java @@ -602,6 +602,21 @@ } @Override + public boolean isServingHTTP() { + return runtimeManager.isServingHTTP(); + } + + @Override + public boolean isServingGIT() { + return runtimeManager.isServingGIT(); + } + + @Override + public boolean isServingSSH() { + return runtimeManager.isServingSSH(); + } + + @Override public TimeZone getTimezone() { return runtimeManager.getTimezone(); } diff --git a/src/main/java/com/gitblit/manager/IRuntimeManager.java b/src/main/java/com/gitblit/manager/IRuntimeManager.java index 29e7368..b2d7a2b 100644 --- a/src/main/java/com/gitblit/manager/IRuntimeManager.java +++ b/src/main/java/com/gitblit/manager/IRuntimeManager.java @@ -57,6 +57,33 @@ boolean isServingRepositories(); /** + * Determine if this Gitblit instance is actively serving git repositories + * over HTTP. + * + * @return true if Gitblit is serving repositories over HTTP + * @since 1.6.0 + */ + boolean isServingHTTP(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the GIT Daemon protocol. + * + * @return true if Gitblit is serving repositories over the GIT Daemon protocol + * @since 1.6.0 + */ + boolean isServingGIT(); + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the SSH protocol. + * + * @return true if Gitblit is serving repositories over the SSH protocol + * @since 1.6.0 + */ + boolean isServingSSH(); + + /** * Determine if this Gitblit instance is running in debug mode * * @return true if Gitblit is running in debug mode diff --git a/src/main/java/com/gitblit/manager/RuntimeManager.java b/src/main/java/com/gitblit/manager/RuntimeManager.java index 52f4d67..9cdc64e 100644 --- a/src/main/java/com/gitblit/manager/RuntimeManager.java +++ b/src/main/java/com/gitblit/manager/RuntimeManager.java @@ -119,9 +119,42 @@ */ @Override public boolean isServingRepositories() { - return settings.getBoolean(Keys.git.enableGitServlet, true) - || (settings.getInteger(Keys.git.daemonPort, 0) > 0) - || (settings.getInteger(Keys.git.sshPort, 0) > 0); + return isServingHTTP() + || isServingGIT() + || isServingSSH(); + } + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the HTTP protocol. + * + * @return true if Gitblit is serving repositories over the HTTP protocol + */ + @Override + public boolean isServingHTTP() { + return settings.getBoolean(Keys.git.enableGitServlet, true); + } + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the Git Daemon protocol. + * + * @return true if Gitblit is serving repositories over the Git Daemon protocol + */ + @Override + public boolean isServingGIT() { + return settings.getInteger(Keys.git.daemonPort, 0) > 0; + } + + /** + * Determine if this Gitblit instance is actively serving git repositories + * over the SSH protocol. + * + * @return true if Gitblit is serving repositories over the SSH protocol + */ + @Override + public boolean isServingSSH() { + return settings.getInteger(Keys.git.sshPort, 0) > 0; } /** diff --git a/src/main/java/com/gitblit/manager/ServicesManager.java b/src/main/java/com/gitblit/manager/ServicesManager.java index 755d8ba..3721578 100644 --- a/src/main/java/com/gitblit/manager/ServicesManager.java +++ b/src/main/java/com/gitblit/manager/ServicesManager.java @@ -112,9 +112,21 @@ } public boolean isServingRepositories() { - return settings.getBoolean(Keys.git.enableGitServlet, true) - || (gitDaemon != null && gitDaemon.isRunning()) - || (sshDaemon != null && sshDaemon.isRunning()); + return isServingHTTP() + || isServingGIT() + || isServingSSH(); + } + + public boolean isServingHTTP() { + return settings.getBoolean(Keys.git.enableGitServlet, true); + } + + public boolean isServingGIT() { + return gitDaemon != null && gitDaemon.isRunning(); + } + + public boolean isServingSSH() { + return sshDaemon != null && sshDaemon.isRunning(); } protected void configureFederation() { diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 8117179..7dc0f9b 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -729,4 +729,12 @@ gb.languagePreference = Language Preference gb.languagePreferenceDescription = Select your preferred translation for the Gitblit UI gb.displayNameDescription = The preferred name for display -gb.emailAddressDescription = The primary email address for receiving notifications \ No newline at end of file +gb.emailAddressDescription = The primary email address for receiving notifications +gb.sshKeys = SSH Keys +gb.sshKeysDescription = SSH public key authentication is a secure alternative to password authentication +gb.addSshKey = Add SSH Key +gb.key = Key +gb.comment = Comment +gb.sshKeyCommentDescription = Enter an optional comment. If blank, the comment will be extracted from the key data. +gb.permission = Permission +gb.sshKeyPermissionDescription = Specify the access permission for the SSH key \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.html b/src/main/java/com/gitblit/wicket/pages/UserPage.html index 0926787..d71cb2b 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.html +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.html @@ -20,6 +20,7 @@ <ul class="nav nav-tabs"> <li class="active"><a href="#repositories" data-toggle="tab"><wicket:message key="gb.repositories"></wicket:message></a></li> <div wicket:id="preferencesLink"></div> + <div wicket:id="sshKeysLink"></div> </ul> <!-- tab content --> @@ -37,6 +38,9 @@ <!-- preferences tab --> <div wicket:id="preferencesTab"></div> + <!-- ssh keys tab --> + <div wicket:id="sshKeysTab"></div> + </div> </div> </div> @@ -45,6 +49,10 @@ <wicket:fragment wicket:id="preferencesLinkFragment"> <li><a href="#preferences" data-toggle="tab"><wicket:message key="gb.preferences"></wicket:message></a></li> +</wicket:fragment> + +<wicket:fragment wicket:id="sshKeysLinkFragment"> + <li><a href="#ssh" data-toggle="tab"><wicket:message key="gb.sshKeys"></wicket:message></a></li> </wicket:fragment> <wicket:fragment wicket:id="preferencesTabFragment"> @@ -63,6 +71,12 @@ </div> </wicket:fragment> +<wicket:fragment wicket:id="sshKeysTabFragment"> + <div class="tab-pane" id="ssh"> + <div wicket:id="sshKeysPanel"></div> + </div> +</wicket:fragment> + </wicket:extend> </body> </html> \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/UserPage.java b/src/main/java/com/gitblit/wicket/pages/UserPage.java index baad4a0..4a955c7 100644 --- a/src/main/java/com/gitblit/wicket/pages/UserPage.java +++ b/src/main/java/com/gitblit/wicket/pages/UserPage.java @@ -50,6 +50,7 @@ import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.ChoiceOption; import com.gitblit.wicket.panels.ProjectRepositoryPanel; +import com.gitblit.wicket.panels.SshKeysPanel; import com.gitblit.wicket.panels.TextOption; import com.gitblit.wicket.panels.UserTitlePanel; @@ -100,10 +101,22 @@ if (isMyProfile) { addPreferences(user); + + if (app().gitblit().isServingSSH()) { + // show the SSH key management tab + addSshKeys(user); + } else { + // SSH daemon is disabled, hide keys tab + add(new Label("sshKeysLink").setVisible(false)); + add(new Label("sshKeysTab").setVisible(false)); + } } else { // visiting user add(new Label("preferencesLink").setVisible(false)); add(new Label("preferencesTab").setVisible(false)); + + add(new Label("sshKeysLink").setVisible(false)); + add(new Label("sshKeysTab").setVisible(false)); } List<RepositoryModel> repositories = getRepositories(params); @@ -251,6 +264,15 @@ add(fragment.setRenderBodyOnly(true)); } + private void addSshKeys(final UserModel user) { + Fragment keysTab = new Fragment("sshKeysTab", "sshKeysTabFragment", this); + keysTab.add(new SshKeysPanel("sshKeysPanel", user, getClass(), getPageParameters())); + + // add the SSH keys tab + add(new Fragment("sshKeysLink", "sshKeysLinkFragment", this).setRenderBodyOnly(true)); + add(keysTab.setRenderBodyOnly(true)); + } + private class Language implements Serializable { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html new file mode 100644 index 0000000..d67b704 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html @@ -0,0 +1,46 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" + xml:lang="en" + lang="en"> + +<body> +<wicket:panel> + <h4><wicket:message key="gb.sshKeys"></wicket:message></h4> + <p><wicket:message key="gb.sshKeysDescription"></wicket:message></p> + <hr /> + + <div wicket:id="keys"> + <div style="display:inline-block;font-size:2em;padding:10px;"> + <i class="fa fa-key"></i> + </div> + <div style="display:inline-block;"> + <div wicket:id="comment" style="font-weight:bold;"></div> + <pre wicket:id="fingerprint"></pre> + </div> + + <div style="display:inline-block;padding: 0px 20px"> + <div wicket:id="permission" style="font-weight:bold;"></div> + <div wicket:id="algorithm"></div> + </div> + + <div style="display:inline-block;vertical-align:text-bottom;"> + <button class="btn btn-danger" wicket:id="delete"><wicket:message key="gb.delete"></wicket:message></button> + </div> + + <hr /> + </div> + + <div class="well"> + <form wicket:id="addKeyForm"> + <h4><wicket:message key="gb.addSshKey"></wicket:message></h4> + <div wicket:id="addKeyData"></div> + <div wicket:id="addKeyPermission"></div> + <div wicket:id="addKeyComment"></div> + + <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Add" wicket:message="value:gb.add" wicket:id="addKeyButton" /></div> + </form> + </div> +</wicket:panel> +</body> +</html> \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java new file mode 100644 index 0000000..03cb93c --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java @@ -0,0 +1,161 @@ +/* + * Copyright 2014 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.wicket.panels; + +import java.util.Arrays; +import java.util.List; + +import org.apache.wicket.PageParameters; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.form.AjaxButton; +import org.apache.wicket.markup.html.WebPage; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.link.Link; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.Model; + +import com.gitblit.Constants.AccessPermission; +import com.gitblit.models.UserModel; +import com.gitblit.transport.ssh.SshKey; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitBlitWebSession; + + +/** + * A panel that enumerates and manages SSH public keys. + * + * @author James Moger + * + */ +public class SshKeysPanel extends BasePanel { + + private static final long serialVersionUID = 1L; + + private final UserModel user; + + private final Class<? extends WebPage> pageClass; + + private final PageParameters params; + + public SshKeysPanel(String wicketId, UserModel user, Class<? extends WebPage> pageClass, PageParameters params) { + super(wicketId); + + this.user = user; + this.pageClass = pageClass; + this.params = params; + } + + @Override + protected void onInitialize() { + super.onInitialize(); + List<SshKey> keys = app().keys().getKeys(user.username); + + final ListDataProvider<SshKey> dp = new ListDataProvider<SshKey>(keys); + DataView<SshKey> keysView = new DataView<SshKey>("keys", dp) { + private static final long serialVersionUID = 1L; + + @Override + public void populateItem(final Item<SshKey> item) { + final SshKey key = item.getModelObject(); + item.add(new Label("comment", key.getComment())); + item.add(new Label("fingerprint", key.getFingerprint())); + item.add(new Label("permission", key.getPermission().toString())); + item.add(new Label("algorithm", key.getAlgorithm())); + + Link<Void> delete = new Link<Void>("delete") { + + private static final long serialVersionUID = 1L; + + @Override + public void onClick() { + if (app().keys().removeKey(user.username, key)) { + setRedirect(true); + setResponsePage(pageClass, params); + } + } + }; + item.add(delete); + } + }; + add(keysView); + + Form<Void> addKeyForm = new Form<Void>("addKeyForm"); + + final IModel<String> keyData = Model.of(""); + addKeyForm.add(new TextAreaOption("addKeyData", + getString("gb.key"), + null, + "span5", + keyData)); + + final IModel<AccessPermission> keyPermission = Model.of(AccessPermission.PUSH); + addKeyForm.add(new ChoiceOption<AccessPermission>("addKeyPermission", + getString("gb.permission"), + getString("gb.sshKeyPermissionDescription"), + keyPermission, + Arrays.asList(AccessPermission.SSHPERMISSIONS))); + + final IModel<String> keyComment = Model.of(""); + addKeyForm.add(new TextOption("addKeyComment", + getString("gb.comment"), + getString("gb.sshKeyCommentDescription"), + "span5", + keyComment)); + + addKeyForm.add(new AjaxButton("addKeyButton") { + + private static final long serialVersionUID = 1L; + + @Override + protected void onSubmit(AjaxRequestTarget target, Form<?> form) { + + UserModel user = GitBlitWebSession.get().getUser(); + String data = keyData.getObject(); + if (StringUtils.isEmpty(data)) { + // do not submit empty key + return; + } + + SshKey key = new SshKey(data); + try { + key.getPublicKey(); + } catch (Exception e) { + // failed to parse the key + return; + } + + AccessPermission permission = keyPermission.getObject(); + key.setPermission(permission); + + String comment = keyComment.getObject(); + if (!StringUtils.isEmpty(comment)) { + key.setComment(comment); + } + + if (app().keys().addKey(user.username, key)) { + setRedirect(true); + setResponsePage(pageClass, params); + } + } + }); + + add(addKeyForm); + } +} diff --git a/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html new file mode 100644 index 0000000..bb7dc7c --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.html @@ -0,0 +1,20 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" + xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd" + xml:lang="en" + lang="en"> + +<body> +<wicket:panel> + <div style="padding-top:4px;"> + <div style="margin-bottom:1px;"> + <b><span wicket:id="name"></span></b> + </div> + <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span> + <p style="padding-top:5px;"><textarea rows="12" class="span5" wicket:id="text"></textarea></p> + </label> + + </div> +</wicket:panel> +</body> +</html> \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java new file mode 100644 index 0000000..d2c74a0 --- /dev/null +++ b/src/main/java/com/gitblit/wicket/panels/TextAreaOption.java @@ -0,0 +1,54 @@ +/* + * Copyright 2014 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.wicket.panels; + +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.model.IModel; + +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.WicketUtils; + +/** + * A re-usable textarea option panel. + * + * title + * description + * [text + * area] + * + * @author James Moger + * + */ +public class TextAreaOption extends BasePanel { + + private static final long serialVersionUID = 1L; + + public TextAreaOption(String wicketId, String title, String description, IModel<String> model) { + this(wicketId, title, description, null, model); + } + + public TextAreaOption(String wicketId, String title, String description, String css, IModel<String> model) { + super(wicketId); + add(new Label("name", title)); + add(new Label("description", description).setVisible(!StringUtils.isEmpty(description))); + TextArea<String> tf = new TextArea<String>("text", model); + if (!StringUtils.isEmpty(css)) { + WicketUtils.setCssClass(tf, css); + } + add(tf); + } +} diff --git a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java index 6e56a87..54be539 100644 --- a/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java +++ b/src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java @@ -82,6 +82,21 @@ } @Override + public boolean isServingHTTP() { + return true; + } + + @Override + public boolean isServingGIT() { + return true; + } + + @Override + public boolean isServingSSH() { + return true; + } + + @Override public boolean isDebugMode() { return true; } -- Gitblit v1.9.1