James Moger
2014-06-05 05f229883c4e15e044c5c103acf69265cfb8806e
Add a basic SSH public key management UI
4 files added
9 files modified
456 ■■■■■ changed files
src/main/java/com/gitblit/GitBlit.java 15 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/GitblitManager.java 15 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/IRuntimeManager.java 27 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/RuntimeManager.java 39 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/manager/ServicesManager.java 18 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/UserPage.html 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/UserPage.java 22 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html 46 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java 161 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/TextAreaOption.html 20 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/TextAreaOption.java 54 ●●●●● patch | view | raw | blame | history
src/test/java/com/gitblit/tests/mock/MockRuntimeManager.java 15 ●●●●● patch | view | raw | blame | history
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()};
    }
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();
    }
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
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;
    }
    /**
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() {
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
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
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>
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;
src/main/java/com/gitblit/wicket/panels/SshKeysPanel.html
New file
@@ -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>
src/main/java/com/gitblit/wicket/panels/SshKeysPanel.java
New file
@@ -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);
    }
}
src/main/java/com/gitblit/wicket/panels/TextAreaOption.html
New file
@@ -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>
src/main/java/com/gitblit/wicket/panels/TextAreaOption.java
New file
@@ -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);
    }
}
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;
    }