Skip to content

Commit

Permalink
Fixes #7 - just tracking as 2 distinct key types for now, actual expi…
Browse files Browse the repository at this point in the history
…ration job has noted complexiities in #8
  • Loading branch information
eddiewebb committed Sep 27, 2015
1 parent ae8fcda commit b5ba55b
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 79 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@ Layers additional controls over -Stash's- Bitbucket's SSH key features that enfo

## Features
- Block unamed keys being added directly to Projects or Repositories

All keys must be created for specific users, and inherit their access.
- Blocks upload of existing keys and generates new RSA 2048 bit keys for the user.

User can download public and private key pair, and regenerate as needed.
Special users designated by a Group may add keys directly, this supports the current Bamboo<>Stash integration which generates user keys when repositories are created in bamboo. This Group should only be grnated to admins or system accounts that provision pipelines.
- Enforces Key expiration policy

To mitigate risk, all user keys are expired after 90 days, and users are notified to re-generate

## Rules
### Key Types
The system recognize 2 key types. (See KeyType enum)
- USER: A key generated by the plugin on user behalf via UI or API.
- BAMBOO: A key presented to stash by a user in the configured 'authorizedGroup'. This group is intended to allow a system ID used in pipeline provisioning to establish a link between Bamboo and Stash.

### Key Limits
Both USER and BAMBOO types allow only a single active Key per user.

### Key Rotation
USER and BAMBOO keys allow separate rotation requirements.


## License
Copyright 2015 Liberty Mutual Insurance

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@

import com.atlassian.stash.ssh.api.SshKey;
import com.atlassian.stash.user.StashUser;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity.KeyType;

public interface EnterpriseKeyRepository {

SshKeyEntity createOrUpdateUserKey(StashUser user, String text, String label);

boolean isValidKeyForUser(StashUser user, String text);

List<SshKeyEntity> listOfExpiredKeyIds(Date oldestValidDate);
List<SshKeyEntity> listOfExpiredKeys(Date oldestValidDate, KeyType keyType);

void updateRecordWithKeyId(SshKeyEntity newRecord, SshKey newKey);

void removeRecord(SshKeyEntity key);

void saveExternallyGeneratedKeyDetails(SshKey key, StashUser stashUser, KeyType bamboo);

}

Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.lmig.forge.stash.ssh.ao;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

Expand All @@ -31,6 +30,7 @@
import com.atlassian.stash.ssh.api.SshKey;
import com.atlassian.stash.user.StashUser;
import com.google.common.collect.Lists;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity.KeyType;

public class EnterpriseKeyRepositoryImpl implements EnterpriseKeyRepository {

Expand All @@ -44,23 +44,35 @@ public EnterpriseKeyRepositoryImpl(ActiveObjects ao) {
}

@Override
public SshKeyEntity createOrUpdateUserKey(StashUser user, String text, String label) {

SshKeyEntity key = findSingleKey(user);
public SshKeyEntity createOrUpdateUserKey(StashUser user, String text, String label) {
SshKeyEntity key = findSingleKey(user,KeyType.USER);
if(null != key){
key.setText(text);
key.setLabel(label);
key.setCreatedDate(new Date());
key.save();
}else{
key = ao.create(SshKeyEntity.class, new DBParam("USERID", user.getId()), new DBParam("TEXT", text),new DBParam("LABEL",label),new DBParam("CREATED", new Date()));
key = ao.create(SshKeyEntity.class, new DBParam("TYPE", SshKeyEntity.KeyType.USER), new DBParam("USERID", user.getId()), new DBParam("TEXT", text),new DBParam("LABEL",label),new DBParam("CREATED", new Date()));
}
return key;

return key;
}


private SshKeyEntity findSingleKey(StashUser user) {
SshKeyEntity[] keys = ao.find(SshKeyEntity.class, Query.select().where("USERID = ?", user.getId()));
@Override
public void saveExternallyGeneratedKeyDetails(SshKey key, StashUser user, KeyType keyType) {
SshKeyEntity entity = findSingleKey(user, KeyType.BAMBOO);
if(null != entity){
entity.setText(key.getText());
entity.setLabel(key.getLabel());
entity.setCreatedDate(new Date());
entity.save();
}else{
entity = ao.create(SshKeyEntity.class, new DBParam("TYPE",keyType), new DBParam("USERID", user.getId()), new DBParam("TEXT", key.getText()),new DBParam("LABEL",key.getLabel()),new DBParam("CREATED", new Date()));
}
}

private SshKeyEntity findSingleKey(StashUser user, KeyType keyType) {
SshKeyEntity[] keys = ao.find(SshKeyEntity.class, Query.select().where("USERID = ? AND TYPE = ?", user.getId(), keyType));
if( null != keys && keys.length == 1 ){
SshKeyEntity key = keys[0];
return key;
Expand All @@ -71,7 +83,7 @@ private SshKeyEntity findSingleKey(StashUser user) {

@Override
public boolean isValidKeyForUser(StashUser user, String text) {
SshKeyEntity key = findSingleKey(user);
SshKeyEntity key = findSingleKey(user, KeyType.USER);
if(null != key){
return key.getText().equals(text);
}
Expand All @@ -80,20 +92,8 @@ public boolean isValidKeyForUser(StashUser user, String text) {
}

@Override
public List<SshKeyEntity> listOfExpiredKeyIds(Date oldestValidDate) {
final List<Integer> expiredIds = new ArrayList<Integer>();

//using ao.stream returns valid OID but all other fields absent.
// ao.stream(SshKeyEntity.class, Query.select().where("CREATED < ?",cal.getTime()),new EntityStreamCallback<SshKeyEntity, Integer>(){
//
// @Override
// public void onRowRead(SshKeyEntity t) {
// log.debug("Reading row: " + t.getID() + "," + t.getLabel() + "," + t.getKeyId());
// expiredIds.add(t.getKeyId());
// }
//
// });
SshKeyEntity[] results = ao.find(SshKeyEntity.class, Query.select().where("CREATED < ?",oldestValidDate));
public List<SshKeyEntity> listOfExpiredKeys(Date oldestValidDate, KeyType keyType) {
SshKeyEntity[] results = ao.find(SshKeyEntity.class, Query.select().where("CREATED < ? and TYPE = ?",oldestValidDate,keyType));
return Lists.newArrayList(results);
}

Expand All @@ -116,6 +116,7 @@ public void removeRecord(SshKeyEntity key) {
//SshKeyEntity[] recordToDelete = ao.find(SshKeyEntity.class, Query.select().where("KEYID = ?",stashKeyId));
ao.delete(key);
}



}
23 changes: 23 additions & 0 deletions src/main/java/com/lmig/forge/stash/ssh/ao/SshKeyEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public interface SshKeyEntity extends Entity{
@NotNull
@Mutator("CREATED")
Date getCreatedDate();
@NotNull
@Mutator("TYPE")
KeyType getKeyType();


@Accessor("KEYID")
Expand All @@ -55,5 +58,25 @@ public interface SshKeyEntity extends Entity{
void setUserId(Integer id);
@Accessor("CREATED")
void setCreatedDate(Date created);
@Accessor("TYPE")
void setKeyType(KeyType type);

public static enum KeyType{
USER("USER","Generated by plugin on behalf of user."),BAMBOO("BAMBOO","Generated on bamboo's die and intercepted by plugin");

private final String name;
private final String description;
KeyType(String name, String description){
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.atlassian.stash.user.UserService;
import com.lmig.forge.stash.ssh.ao.EnterpriseKeyRepository;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity.KeyType;
import com.lmig.forge.stash.ssh.config.PluginSettingsService;
import com.lmig.forge.stash.ssh.crypto.SshKeyPairGenerator;
import com.lmig.forge.stash.ssh.notifications.NotificationService;
Expand Down Expand Up @@ -60,6 +61,7 @@ public boolean isKeyValidForUser(SshKey key, StashUser stashUser) {
//allow bamboo <> stash keys for system accounts in special group.
String userGroup = pluginSettingsService.getAuthorizedGroup();
if( userGroup != null && userService.existsGroup(userGroup) && userService.isUserInGroup(stashUser, userGroup)){
enterpriseKeyRepository.saveExternallyGeneratedKeyDetails(key,stashUser,SshKeyEntity.KeyType.BAMBOO);
return true;
}else{
//otherwise user must have gone through our wrapper
Expand Down Expand Up @@ -90,13 +92,15 @@ public KeyPairResourceModel generateNewKeyPairFor(StashUser user) {
enterpriseKeyRepository.updateRecordWithKeyId(newRecord, newKey);
return result;
}



@Override
public void replaceExpiredKeysAndNotifyUsers() {
DateTime dateTime = new DateTime();

//cal.add(Calendar.MINUTE, -1); //live demo in UI.
List<SshKeyEntity> expiredStashKeys = enterpriseKeyRepository.listOfExpiredKeyIds( dateTime.minusDays(pluginSettingsService.getDaysAllowedForUserKeys()).toDate());
List<SshKeyEntity> expiredStashKeys = enterpriseKeyRepository.listOfExpiredKeys( dateTime.minusDays(pluginSettingsService.getDaysAllowedForUserKeys()).toDate(), KeyType.USER);


for (SshKeyEntity keyRecord : expiredStashKeys) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Calendar;

import net.java.ao.DBParam;
import net.java.ao.EntityManager;
import net.java.ao.test.jdbc.Data;
Expand All @@ -52,6 +48,7 @@
import com.lmig.forge.stash.ssh.ao.EnterpriseKeyRepository;
import com.lmig.forge.stash.ssh.ao.EnterpriseKeyRepositoryImpl;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity;
import com.lmig.forge.stash.ssh.ao.SshKeyEntity.KeyType;
import com.lmig.forge.stash.ssh.config.PluginSettingsService;
import com.lmig.forge.stash.ssh.keys.EnterpriseSshKeyService;
import com.lmig.forge.stash.ssh.keys.EnterpriseSshKeyServiceImpl;
Expand Down Expand Up @@ -201,11 +198,11 @@ public void update(EntityManager em) throws Exception {
// create a pre-expired key in DB for scheduler
DateTime now = new DateTime();
expiredKey = em.create(SshKeyEntity.class, new DBParam("USERID", EXPIRED_USER_ID), new DBParam("KEYID",
EXPIRED_STASH_KEY_ID), new DBParam("TEXT", APPROVED_PUBLIC_KEY_ONE), new DBParam("LABEL", "COMPROMISED"),
EXPIRED_STASH_KEY_ID), new DBParam("TEXT", APPROVED_PUBLIC_KEY_ONE), new DBParam("LABEL", "COMPROMISED"), new DBParam("TYPE", KeyType.USER),
new DBParam("CREATED", now.minusDays(DAYS_ALLOWED+1).toDate()));

validKey = em.create(SshKeyEntity.class, new DBParam("USERID", VALID_USER_ID), new DBParam("KEYID",
VALID_STASH_KEY_ID), new DBParam("TEXT", APPROVED_PUBLIC_KEY_ONE), new DBParam("LABEL", "VALID"),
VALID_STASH_KEY_ID), new DBParam("TEXT", APPROVED_PUBLIC_KEY_ONE), new DBParam("LABEL", "VALID"), new DBParam("TYPE", KeyType.USER),
new DBParam("CREATED", now.minusDays(DAYS_ALLOWED-1).toDate()));

}
Expand Down
Loading

0 comments on commit b5ba55b

Please sign in to comment.