Browse Source

Support specifying permission levels for SSH public keys

tags/v1.5.0
James Moger 10 years ago
parent
commit
c78b25d102

+ 2
- 0
src/main/java/com/gitblit/Constants.java View File

@@ -423,6 +423,8 @@ public class Constants {
public static final AccessPermission [] NEWPERMISSIONS = { EXCLUDE, VIEW, CLONE, PUSH, CREATE, DELETE, REWIND };
public static final AccessPermission [] SSHPERMISSIONS = { VIEW, CLONE, PUSH };
public static AccessPermission LEGACY = REWIND;
public final String code;

+ 32
- 16
src/main/java/com/gitblit/transport/ssh/FileKeyManager.java View File

@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.gitblit.Constants.AccessPermission;
import com.gitblit.Keys;
import com.gitblit.manager.IRuntimeManager;
import com.google.common.base.Charsets;
@@ -105,8 +106,18 @@ public class FileKeyManager extends IPublicKeyManager {
// skip comments
continue;
}
SshKey key = new SshKey(entry);
list.add(key);
String [] parts = entry.split(" ", 2);
AccessPermission perm = AccessPermission.fromCode(parts[0]);
if (perm.equals(AccessPermission.NONE)) {
// ssh-rsa DATA COMMENT
SshKey key = new SshKey(entry);
list.add(key);
} else if (perm.exceeds(AccessPermission.NONE)) {
// PERMISSION ssh-rsa DATA COMMENT
SshKey key = new SshKey(parts[1]);
key.setPermission(perm);
list.add(key);
}
}

if (list.isEmpty()) {
@@ -129,7 +140,6 @@ public class FileKeyManager extends IPublicKeyManager {
@Override
public boolean addKey(String username, SshKey key) {
try {
String newKey = stripCommentFromKey(key.getRawData());
boolean replaced = false;
List<String> lines = new ArrayList<String>();
File keystore = getKeystore(username);
@@ -147,10 +157,10 @@ public class FileKeyManager extends IPublicKeyManager {
continue;
}

String oldKey = stripCommentFromKey(line);
if (newKey.equals(oldKey)) {
SshKey oldKey = parseKey(line);
if (key.equals(oldKey)) {
// replace key
lines.add(key.getRawData());
lines.add(key.getPermission() + " " + key.getRawData());
replaced = true;
} else {
// retain key
@@ -161,7 +171,7 @@ public class FileKeyManager extends IPublicKeyManager {

if (!replaced) {
// new key, append
lines.add(key.getRawData());
lines.add(key.getPermission() + " " + key.getRawData());
}

// write keystore
@@ -182,8 +192,6 @@ public class FileKeyManager extends IPublicKeyManager {
@Override
public boolean removeKey(String username, SshKey key) {
try {
String rmKey = stripCommentFromKey(key.getRawData());

File keystore = getKeystore(username);
if (keystore.exists()) {
List<String> lines = new ArrayList<String>();
@@ -201,8 +209,8 @@ public class FileKeyManager extends IPublicKeyManager {
}

// only include keys that are NOT rmKey
String oldKey = stripCommentFromKey(line);
if (!rmKey.equals(oldKey)) {
SshKey oldKey = parseKey(line);
if (!key.equals(oldKey)) {
lines.add(entry);
}
}
@@ -242,10 +250,18 @@ public class FileKeyManager extends IPublicKeyManager {
return keys;
}

/* Strips the comment from the key data and eliminates whitespace diffs */
protected String stripCommentFromKey(String data) {
String [] cols = data.split(" ", 3);
String key = Joiner.on(" ").join(cols[0], cols[1]);
return key;
protected SshKey parseKey(String line) {
String [] parts = line.split(" ", 2);
AccessPermission perm = AccessPermission.fromCode(parts[0]);
if (perm.equals(AccessPermission.NONE)) {
// ssh-rsa DATA COMMENT
SshKey key = new SshKey(line);
return key;
} else {
// PERMISSION ssh-rsa DATA COMMENT
SshKey key = new SshKey(parts[1]);
key.setPermission(perm);
return key;
}
}
}

+ 47
- 0
src/main/java/com/gitblit/transport/ssh/SshKey.java View File

@@ -2,12 +2,15 @@ package com.gitblit.transport.ssh;

import java.io.Serializable;
import java.security.PublicKey;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.codec.binary.Base64;
import org.apache.sshd.common.SshException;
import org.apache.sshd.common.util.Buffer;
import org.eclipse.jgit.lib.Constants;

import com.gitblit.Constants.AccessPermission;
import com.gitblit.utils.StringUtils;

/**
@@ -30,13 +33,17 @@ public class SshKey implements Serializable {

private String toString;

private AccessPermission permission;

public SshKey(String data) {
this.rawData = data;
this.permission = AccessPermission.PUSH;
}

public SshKey(PublicKey key) {
this.publicKey = key;
this.comment = "";
this.permission = AccessPermission.PUSH;
}

public PublicKey getPublicKey() {
@@ -78,6 +85,46 @@ public class SshKey implements Serializable {
}
}

/**
* Returns true if this key may be used to clone or fetch.
*
* @return true if this key can be used to clone or fetch
*/
public boolean canClone() {
return permission.atLeast(AccessPermission.CLONE);
}

/**
* Returns true if this key may be used to push changes.
*
* @return true if this key can be used to push changes
*/
public boolean canPush() {
return permission.atLeast(AccessPermission.PUSH);
}

/**
* Returns the access permission for the key.
*
* @return the access permission for the key
*/
public AccessPermission getPermission() {
return permission;
}

/**
* Control the access permission assigned to this key.
*
* @param value
*/
public void setPermission(AccessPermission value) throws IllegalArgumentException {
List<AccessPermission> permitted = Arrays.asList(AccessPermission.SSHPERMISSIONS);
if (!permitted.contains(value)) {
throw new IllegalArgumentException("Illegal SSH public key permission specified: " + value);
}
this.permission = value;
}

public String getRawData() {
if (rawData == null && publicKey != null) {
// build the raw data manually from the public key

+ 5
- 0
src/main/java/com/gitblit/transport/ssh/git/Receive.java View File

@@ -17,12 +17,17 @@ package com.gitblit.transport.ssh.git;

import org.eclipse.jgit.transport.ReceivePack;

import com.gitblit.transport.ssh.SshKey;
import com.gitblit.transport.ssh.commands.CommandMetaData;

@CommandMetaData(name = "git-receive-pack", description = "Receives pushes from a client", hidden = true)
public class Receive extends BaseGitCommand {
@Override
protected void runImpl() throws Failure {
SshKey key = getContext().getClient().getKey();
if (key != null && !key.canPush()) {
throw new Failure(1, "Sorry, your SSH public key is not allowed to push changes!");
}
try {
ReceivePack rp = receivePackFactory.create(getContext().getClient(), repo);
rp.receive(in, out, null);

+ 5
- 0
src/main/java/com/gitblit/transport/ssh/git/Upload.java View File

@@ -17,6 +17,7 @@ package com.gitblit.transport.ssh.git;

import org.eclipse.jgit.transport.UploadPack;

import com.gitblit.transport.ssh.SshKey;
import com.gitblit.transport.ssh.commands.CommandMetaData;

@CommandMetaData(name = "git-upload-pack", description = "Sends packs to a client for clone and fetch", hidden = true)
@@ -24,6 +25,10 @@ public class Upload extends BaseGitCommand {
@Override
protected void runImpl() throws Failure {
try {
SshKey key = getContext().getClient().getKey();
if (key != null && !key.canClone()) {
throw new Failure(1, "Sorry, your SSH public key is not allowed to clone!");
}
UploadPack up = uploadPackFactory.create(getContext().getClient(), repo);
up.upload(in, out, null);
} catch (Exception e) {

+ 29
- 5
src/main/java/com/gitblit/transport/ssh/keys/KeysDispatcher.java View File

@@ -24,6 +24,7 @@ import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants.AccessPermission;
import com.gitblit.models.UserModel;
import com.gitblit.transport.ssh.IPublicKeyManager;
import com.gitblit.transport.ssh.SshKey;
@@ -33,6 +34,7 @@ import com.gitblit.transport.ssh.commands.SshCommand;
import com.gitblit.transport.ssh.commands.UsageExample;
import com.gitblit.utils.FlipTable;
import com.gitblit.utils.FlipTable.Borders;
import com.gitblit.utils.StringUtils;
import com.google.common.base.Joiner;

/**
@@ -54,7 +56,7 @@ public class KeysDispatcher extends DispatchCommand {
}

@CommandMetaData(name = "add", description = "Add an SSH public key to your account")
@UsageExample(syntax = "cat ~/.ssh/id_rsa.pub | ${ssh} ${cmd} -", description = "Upload your SSH public key and add it to your account")
@UsageExample(syntax = "cat ~/.ssh/id_rsa.pub | ${ssh} ${cmd}", description = "Upload your SSH public key and add it to your account")
public static class AddKey extends BaseKeyCommand {

protected final Logger log = LoggerFactory.getLogger(getClass());
@@ -62,12 +64,33 @@ public class KeysDispatcher extends DispatchCommand {
@Argument(metaVar = "<KEY>", usage = "the key(s) to add")
private List<String> addKeys = new ArrayList<String>();

@Option(name = "--permission", aliases = { "-p" }, metaVar = "PERMISSION", usage = "set the key access permission")
private String permission;

@Override
protected String getUsageText() {
String permissions = Joiner.on(", ").join(AccessPermission.SSHPERMISSIONS);
StringBuilder sb = new StringBuilder();
sb.append("Valid SSH public key permissions are:\n ").append(permissions);
return sb.toString();
}

@Override
public void run() throws IOException, UnloggedFailure {
String username = getContext().getClient().getUsername();
List<String> keys = readKeys(addKeys);
for (String key : keys) {
SshKey sshKey = parseKey(key);
if (!StringUtils.isEmpty(permission)) {
AccessPermission ap = AccessPermission.fromCode(permission);
if (ap.exceeds(AccessPermission.NONE)) {
try {
sshKey.setPermission(ap);
} catch (IllegalArgumentException e) {
throw new UnloggedFailure(1, e.getMessage());
}
}
}
getKeyManager().addKey(username, sshKey);
log.info("added SSH public key for {}", username);
}
@@ -167,14 +190,15 @@ public class KeysDispatcher extends DispatchCommand {
}

protected void asTable(List<SshKey> keys) {
String[] headers = { "#", "Fingerprint", "Comment", "Type" };
String[] headers = { "#", "Fingerprint", "Comment", "Permission", "Type" };
int len = keys == null ? 0 : keys.size();
Object[][] data = new Object[len][];
for (int i = 0; i < len; i++) {
// show 1-based index numbers with the fingerprint
// this is useful for comparing with "ssh-add -l"
SshKey k = keys.get(i);
data[i] = new Object[] { (i + 1), k.getFingerprint(), k.getComment(), k.getAlgorithm() };
data[i] = new Object[] { (i + 1), k.getFingerprint(), k.getComment(),
k.getPermission(), k.getAlgorithm() };
}

stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS));
@@ -211,9 +235,9 @@ public class KeysDispatcher extends DispatchCommand {
}

protected void asTable(int index, SshKey key) {
String[] headers = { "#", "Fingerprint", "Comment", "Type" };
String[] headers = { "#", "Fingerprint", "Comment", "Permission", "Type" };
Object[][] data = new Object[1][];
data[0] = new Object[] { index, key.getFingerprint(), key.getComment(), key.getAlgorithm() };
data[0] = new Object[] { index, key.getFingerprint(), key.getComment(), key.getPermission(), key.getAlgorithm() };

stdout.println(FlipTable.of(headers, data, Borders.BODY_HCOLS));
}

Loading…
Cancel
Save