Skip to content

Commit

Permalink
Fixes #2 by exposing simple REST endpoint with interval, authorized g…
Browse files Browse the repository at this point in the history
…roupm and day limits for user and system keys
  • Loading branch information
eddiewebb committed Sep 26, 2015
1 parent 1da8f7c commit ae8fcda
Show file tree
Hide file tree
Showing 25 changed files with 508 additions and 189 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.lmig.forge.stash.ssh;

import java.security.NoSuchAlgorithmException;

public class EnterpriseKeyGenerationException extends RuntimeException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@
package com.lmig.forge.stash.ssh.ao;

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

import net.java.ao.DBParam;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.lmig.forge.stash.ssh.config;

import javax.transaction.Transactional;

import org.apache.commons.lang.Validate;

import com.atlassian.sal.api.pluginsettings.PluginSettings;
import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
import com.lmig.forge.stash.ssh.rest.AdminConfigResourceModel;

public class PluginSettingsService {
public static final String SETTINGS_KEY_AUTHORIZED_GROUP = AdminConfigResourceModel.class.getName() + ".authorizedGroup";
public static final String SETTINGS_KEY_DAYS_KEEP_USERS = AdminConfigResourceModel.class.getName() + ".daysToKeepUserKeys";
public static final String SETTINGS_KEY_DAYS_KEEP_BAMBOO = AdminConfigResourceModel.class.getName() + ".daysToKeepBambooKeys";
public static final String SETTINGS_KEY_MILLIS_INTERVAL = AdminConfigResourceModel.class.getName() + ".millisBetweenRuns";

private final PluginSettingsFactory pluginSettingsFactory;
private AdminConfigResourceModel cachedModel;

public PluginSettingsService(PluginSettingsFactory pluginSettingsFactory) {
this.pluginSettingsFactory = pluginSettingsFactory;
}

@Transactional
public AdminConfigResourceModel getAdminConfigResourcesModel(){
if(null != cachedModel){
return cachedModel;
}
PluginSettings settings = pluginSettingsFactory.createGlobalSettings();
AdminConfigResourceModel config = new AdminConfigResourceModel();

String authorizedGroup = (String) settings.get(SETTINGS_KEY_AUTHORIZED_GROUP);
if (authorizedGroup != null)
{
config.setAuthorizedGroup(authorizedGroup);
}

String daysToKeepUserKeys = (String) settings.get(SETTINGS_KEY_DAYS_KEEP_USERS);
if (daysToKeepUserKeys != null)
{
config.setDaysToKeepUserKeys(Integer.parseInt(daysToKeepUserKeys));
}
String daysToKeepBambooKeys = (String) settings.get(SETTINGS_KEY_DAYS_KEEP_BAMBOO);
if (daysToKeepBambooKeys != null)
{
config.setDaysToKeepBambooKeys(Integer.parseInt(daysToKeepBambooKeys));
}
String millisBetweenRuns = (String) settings.get(SETTINGS_KEY_MILLIS_INTERVAL);
if (millisBetweenRuns != null)
{
config.setMillisBetweenRuns(Long.parseLong(millisBetweenRuns));
}
return config;
}

@Transactional
public AdminConfigResourceModel updateAdminConfigResourcesModel(final AdminConfigResourceModel updatedConfig) {

Validate.isTrue(updatedConfig.getDaysToKeepBambooKeys() >= 1,"must keep keys at least 1 day");
Validate.isTrue(updatedConfig.getDaysToKeepUserKeys() >= 1,"must keep keys at least 1 day");
Validate.isTrue(updatedConfig.getMillisBetweenRuns() >= 60000,"Must space at least 1 minute apart");
PluginSettings settings = pluginSettingsFactory.createGlobalSettings();

settings.put(SETTINGS_KEY_AUTHORIZED_GROUP, updatedConfig.getAuthorizedGroup());
settings.put(SETTINGS_KEY_DAYS_KEEP_USERS, String.valueOf(updatedConfig.getDaysToKeepUserKeys()));
settings.put(SETTINGS_KEY_DAYS_KEEP_BAMBOO, String.valueOf(updatedConfig.getDaysToKeepBambooKeys()));
settings.put(SETTINGS_KEY_MILLIS_INTERVAL, String.valueOf(updatedConfig.getMillisBetweenRuns()));

//refresh cache, including logic on default values in case some were not provided.
cachedModel = updatedConfig;
return cachedModel;

}

public String getAuthorizedGroup() {
return getAdminConfigResourcesModel().getAuthorizedGroup();
}

public int getDaysAllowedForUserKeys() {
return getAdminConfigResourcesModel().getDaysToKeepUserKeys();
}
public int getDaysAllowedForBambooKeys() {
return getAdminConfigResourcesModel().getDaysToKeepBambooKeys();
}
public long getMillisBetweenRuns() {
return getAdminConfigResourcesModel().getMillisBetweenRuns();
}

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

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

import com.atlassian.stash.user.StashUser;
import com.lmig.forge.stash.ssh.rest.KeyPairResourceModel;

public interface SshKeyPairGenerator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@

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

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -30,36 +28,38 @@
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.config.PluginSettingsService;
import com.lmig.forge.stash.ssh.crypto.SshKeyPairGenerator;
import com.lmig.forge.stash.ssh.notifications.NotificationService;
import com.lmig.forge.stash.ssh.rest.KeyPairResourceModel;

public class EnterpriseSshKeyServiceImpl implements EnterpriseSshKeyService {
final private static int NINETY_DAYS = 90;
final public static String ALLOWED_SSH_GROUP ="ssh-gods";
final private SshKeyService sshKeyService;
final private EnterpriseKeyRepository enterpriseKeyRepository;
final private SshKeyPairGenerator sshKeyPairGenerator;
final private NotificationService notificationService;
final private UserService userService;
final private PluginSettingsService pluginSettingsService;


private static final Logger log = LoggerFactory.getLogger(EnterpriseSshKeyServiceImpl.class);

public EnterpriseSshKeyServiceImpl(SshKeyService sshKeyService, EnterpriseKeyRepository enterpriseKeyRepository,
SshKeyPairGenerator sshKeyPairGenerator, NotificationService notificationService, UserService userService) {
SshKeyPairGenerator sshKeyPairGenerator, NotificationService notificationService, UserService userService,PluginSettingsService pluginSettingsService) {
this.sshKeyService = sshKeyService;
this.enterpriseKeyRepository = enterpriseKeyRepository;
this.sshKeyPairGenerator = sshKeyPairGenerator;
this.notificationService = notificationService;
this.userService = userService;
this.pluginSettingsService = pluginSettingsService;

}

@Override
public boolean isKeyValidForUser(SshKey key, StashUser stashUser) {
//allow bamboo <> stash keys for system accounts in special group.
if( userService.existsGroup(ALLOWED_SSH_GROUP) && userService.isUserInGroup(stashUser, ALLOWED_SSH_GROUP)){
String userGroup = pluginSettingsService.getAuthorizedGroup();
if( userGroup != null && userService.existsGroup(userGroup) && userService.isUserInGroup(stashUser, userGroup)){
return true;
}else{
//otherwise user must have gone through our wrapper
Expand Down Expand Up @@ -93,12 +93,10 @@ public KeyPairResourceModel generateNewKeyPairFor(StashUser user) {

@Override
public void replaceExpiredKeysAndNotifyUsers() {
Date today = new Date();
Calendar cal = new GregorianCalendar();
cal.setTime(today);
cal.add(Calendar.DAY_OF_YEAR, -NINETY_DAYS);
DateTime dateTime = new DateTime();

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


for (SshKeyEntity keyRecord : expiredStashKeys) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import com.atlassian.stash.i18n.I18nService;
import com.atlassian.stash.mail.MailMessage;
import com.atlassian.stash.mail.MailService;
import com.atlassian.stash.mail.SoyMailBuilder;
import com.atlassian.stash.server.ApplicationPropertiesService;
import com.atlassian.stash.user.StashUser;
import com.atlassian.stash.user.UserService;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.lmig.forge.stash.ssh.rest;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import com.atlassian.sal.api.user.UserManager;
import com.atlassian.stash.user.StashAuthenticationContext;
import com.atlassian.stash.user.StashUser;
import com.lmig.forge.stash.ssh.config.PluginSettingsService;

/**
* A resource of message.
*/
@Path("/config")
public class AdminConfigResource {

private final UserManager userManager;
private final StashAuthenticationContext stashAuthenticationContext;
private final PluginSettingsService pluginSettingsService;


public AdminConfigResource(UserManager userManager, StashAuthenticationContext stashAuthenticationContext,
PluginSettingsService pluginSettingsService) {
super();
this.userManager = userManager;
this.stashAuthenticationContext = stashAuthenticationContext;
this.pluginSettingsService = pluginSettingsService;
}

@GET
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response getConfig()
{
StashUser user = stashAuthenticationContext.getCurrentUser();
if (user == null || !userManager.isSystemAdmin(user.getName())){
return Response.status(Response.Status.FORBIDDEN).build();
}
return Response.ok( pluginSettingsService.getAdminConfigResourcesModel()).build();
}

@PUT
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Response updateConfig(AdminConfigResourceModel updatedModel)
{
StashUser user = stashAuthenticationContext.getCurrentUser();
if (user == null || !userManager.isSystemAdmin(user.getName())){
return Response.status(Response.Status.FORBIDDEN).build();
}

return Response.ok( pluginSettingsService.updateAdminConfigResourcesModel(updatedModel)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.lmig.forge.stash.ssh.rest;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "config")
@XmlAccessorType(XmlAccessType.FIELD)
public class AdminConfigResourceModel {

public static final int DEFAULT_DAYS_USER = 90;
public static final int DEFAULT_DAYS_BAMBOO = 365;
public static final long DEFAULT_MILLIS_BETWEEN_RUNS = 1000 * 60 * 60 * 24; //one day
@XmlElement(name = "message")
private String message;
@XmlElement(name = "authorizedGroup")
private String authorizedGroup;
@XmlElement(name = "daysToKeepUserKeys")
private int daysToKeepUserKeys = DEFAULT_DAYS_USER;
@XmlElement(name = "daysToKeepBambooKeys")
private int daysToKeepBambooKeys = DEFAULT_DAYS_BAMBOO;
@XmlElement(name = "millisBetweenRuns")
private long millisBetweenRuns = DEFAULT_MILLIS_BETWEEN_RUNS;

public AdminConfigResourceModel() {
}

public AdminConfigResourceModel(String message, String authorizedGroup, int daysToKeepUserKeys,
int daysToKeepBambooKeys, long millisBetweenRuns) {
this.message = message;
this.authorizedGroup = authorizedGroup;
this.daysToKeepUserKeys = daysToKeepUserKeys;
this.daysToKeepBambooKeys = daysToKeepBambooKeys;
this.millisBetweenRuns = millisBetweenRuns;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String getAuthorizedGroup() {
return authorizedGroup;
}

public void setAuthorizedGroup(String authorizedGroup) {
this.authorizedGroup = authorizedGroup;
}

public int getDaysToKeepUserKeys() {
return daysToKeepUserKeys;
}

public void setDaysToKeepUserKeys(int daysToKeepUserKeys) {
this.daysToKeepUserKeys = daysToKeepUserKeys;
}

public int getDaysToKeepBambooKeys() {
return daysToKeepBambooKeys;
}

public void setDaysToKeepBambooKeys(int daysToKeepBambooKeys) {
this.daysToKeepBambooKeys = daysToKeepBambooKeys;
}

public long getMillisBetweenRuns() {
return millisBetweenRuns;
}

public void setMillisBetweenRuns(long millisBetweenRuns) {
this.millisBetweenRuns = millisBetweenRuns;
}

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

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

import java.security.KeyPair;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package com.lmig.forge.stash.ssh.scheduler;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -31,32 +30,36 @@
import com.atlassian.scheduler.config.JobRunnerKey;
import com.atlassian.scheduler.config.RunMode;
import com.atlassian.scheduler.config.Schedule;
import com.lmig.forge.stash.ssh.config.PluginSettingsService;


public class KeyRotationScheduler implements DisposableBean, InitializingBean {

private static final JobId JOB_ID = JobId.of("com.lmig.forge.stash:stash-ssh-key-enforcer:KeyRotationJob");
private static final long JOB_INTERVAL = TimeUnit.DAYS.toMillis(1L); //TODO runs daily, add config
//private static final long JOB_INTERVAL = TimeUnit.MINUTES.toMillis(1L);//live demo, expires on the minute.
private static final String JOB_RUNNER_KEY = "com.lmig.forge.stash:stash-ssh-key-enforcer:KeyRotationJobRunner";
private static final Logger log = LoggerFactory.getLogger(KeyRotationScheduler.class);

private final SchedulerService schedulerService;
private KeyRotationJobRunner keyRotationJobRunner;
private final KeyRotationJobRunner keyRotationJobRunner;
private final PluginSettingsService pluginSettingsService;

public KeyRotationScheduler(SchedulerService schedulerService,KeyRotationJobRunner keyRotationJobRunner) {
public KeyRotationScheduler(SchedulerService schedulerService,KeyRotationJobRunner keyRotationJobRunner,PluginSettingsService pluginSettingsService) {
this.schedulerService = schedulerService;
this.keyRotationJobRunner = keyRotationJobRunner;
this.pluginSettingsService = pluginSettingsService;
}

@Override
/**
* this occurs on start and when the settings/config of server are updated. Includes any changes made via {@link PluginSettingsService}
*/
public void afterPropertiesSet() throws SchedulerServiceException {
//The JobRunner could be another component injected in the constructor, a
//private nested class, etc. It just needs to implement JobRunner
schedulerService.registerJobRunner(JobRunnerKey.of(JOB_RUNNER_KEY), keyRotationJobRunner);
schedulerService.scheduleJob(JOB_ID, JobConfig.forJobRunnerKey(JobRunnerKey.of(JOB_RUNNER_KEY))
.withRunMode(RunMode.RUN_ONCE_PER_CLUSTER)
.withSchedule(Schedule.forInterval(JOB_INTERVAL, new Date(System.currentTimeMillis() + JOB_INTERVAL))));
.withSchedule(Schedule.forInterval(pluginSettingsService.getMillisBetweenRuns(), new Date(System.currentTimeMillis() + pluginSettingsService.getMillisBetweenRuns()))));
log.debug("KEY Expiring Job Scheduled");
}

Expand Down
Loading

0 comments on commit ae8fcda

Please sign in to comment.