Skip to content

Commit

Permalink
implementing an endpoint for triggering workspace synchronizations
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusoe committed Jul 20, 2021
1 parent 388f436 commit ea99ed5
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 1 deletion.
@@ -1,13 +1,22 @@
package rocks.inspectit.ocelot.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.CollectionUtils;
import rocks.inspectit.ocelot.config.model.InspectitServerSettings;
import rocks.inspectit.ocelot.config.model.SecuritySettings;
import rocks.inspectit.ocelot.filters.WebhookAccessFilter;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

@Slf4j
@Configuration
@EnableScheduling
public class BeanConfiguration {
Expand All @@ -23,4 +32,34 @@ public class BeanConfiguration {
public ScheduledExecutorService fixedThreadPool(InspectitServerSettings config) {
return Executors.newScheduledThreadPool(config.getThreadPoolSize());
}

/**
* Creates the {@link WebhookAccessFilter} which handles the token authentication against the webhook endpoints.
*
* @param settings The currently used server settings.
*
* @return the {@link WebhookAccessFilter}
*/
@Bean
@ConditionalOnExpression("'${inspectit-config-server.security.webhook-tokens}' != null")
public FilterRegistrationBean<WebhookAccessFilter> webhookAccessFilter(InspectitServerSettings settings) {
List<String> validTokens;
if (settings.getSecurity() != null) {
validTokens = settings.getSecurity().getWebhookTokens();
} else {
validTokens = Collections.emptyList();
}

if (CollectionUtils.isEmpty(validTokens)) {
log.warn("Requests against webhook endpoints will be rejected because no access-tokens have been specified. See the documentation on how to specify access-tokens.");
}

WebhookAccessFilter accessFilter = new WebhookAccessFilter(validTokens);

FilterRegistrationBean<WebhookAccessFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(accessFilter);
registrationBean.addUrlPatterns("/api/v1/hook/*");

return registrationBean;
}
}
Expand Up @@ -7,6 +7,8 @@

import javax.validation.Valid;
import javax.validation.constraints.AssertFalse;
import java.util.Collections;
import java.util.List;

/**
* Security settings of the configuration server.
Expand Down Expand Up @@ -42,6 +44,12 @@ public class SecuritySettings {
@Builder.Default
private boolean fourEyesPromotion = false;

/**
* Valid tokens which can be used to authorize calls against the '/api/v1/hooks/**' endpoints.
*/
@Builder.Default
private List<String> webhookTokens = Collections.emptyList();

/**
* Verify that LDAP settings exist if LDAP is enabled.
*/
Expand Down
Expand Up @@ -193,4 +193,12 @@ public List<WorkspaceVersion> listWorkspaceVersions() throws IOException, GitAPI
}
return workspaceVersions;
}

/**
* Synchronizes the local working directory with a configured remote configuration source.
*/
public void synchronizeWorkspace() throws GitAPIException, IOException {
log.info("Synchronizing configuration workspace by pulling in remote configuration source.");
versioningManager.pullSourceBranch();
}
}
Expand Up @@ -796,6 +796,28 @@ public List<WorkspaceVersion> listWorkspaceVersions() throws IOException, GitAPI
.collect(Collectors.toList());
}

/**
* Synchronizes the local workspace branch with the configured remote configuration source. The synchronization
* is only done in case it is configured and enabled. In this case, the configured remote will be fetched and its
* branch merged into the local workspace. Optionally, the modifications are promoted into the live branch.
*
* @return true in case the synchronization has been done.
*/
public synchronized boolean pullSourceBranch() throws GitAPIException, IOException {
RemoteConfigurationsSettings remoteSettings = settings.getRemoteConfigurations();

if (remoteSettings == null || !remoteSettings.isEnabled() || remoteSettings.getSourceRepository() == null) {
log.info("Remote configuration source will not be pulled because it is not specified or disabled.");
return false;
}

// fetch and merge the remote source into the local workspace
remoteConfigurationManager.fetchSourceBranch(remoteSettings.getSourceRepository());
mergeSourceBranch();

return true;
}

/**
* Merges the configured configurations remote source branch into the local workspace branch.
*/
Expand Down
@@ -0,0 +1,45 @@
package rocks.inspectit.ocelot.filters;

import lombok.AllArgsConstructor;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
* Filter for handling token authentication of the webhook endpoints. In case no token or only blank tokens are defined,
* access will always be denied.
*/
@Slf4j
@AllArgsConstructor
public class WebhookAccessFilter implements Filter {

/**
* The name of the query parameter containing the token.
*/
private static final String TOKEN_PARAMETER = "token";

/**
* A list of valid tokens.
*/
@NonNull
private final List<String> validTokens;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
String secret = request.getParameter(TOKEN_PARAMETER);

if (StringUtils.isNotBlank(secret) && validTokens.contains(secret)) {
chain.doFilter(request, response);
} else {
String requestUrl = ((HttpServletRequest) request).getRequestURL().toString();
log.warn("Access has been denied for '{}' because of an invalid or missing token.", requestUrl);
((HttpServletResponse) response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
}
@@ -0,0 +1,29 @@
package rocks.inspectit.ocelot.rest.hook;

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import rocks.inspectit.ocelot.file.FileManager;
import rocks.inspectit.ocelot.rest.AbstractBaseController;

import java.io.IOException;

@RestController
public class WebhookController extends AbstractBaseController {

@Autowired
private FileManager fileManager;

@ApiOperation(value = "Triggers a synchronization of the workspace branch with a configured remote configuration source.")
@ApiImplicitParam(name = "token", type = "string", value = "Token for authenticating the request.")
@GetMapping(value = "hook/synchronize-workspace")
public ResponseEntity<?> synchronizeWorkspace() throws GitAPIException, IOException {
fileManager.synchronizeWorkspace();

return ResponseEntity.ok().build();
}
}
Expand Up @@ -65,7 +65,8 @@ public void configure(WebSecurity web) {
"/swagger*/**",
"/webjars/**",
"/api/v1/agent/configuration",
"/api/v1/agent/command");
"/api/v1/agent/command",
"/api/v1/hook/**");
}

@Override
Expand Down
Expand Up @@ -50,6 +50,11 @@ inspectit-config-server:
# If enabled, non-admin users cannot promote their own changes.
# The writing of the configuration and the promotion needs to be done by two separate persons.
four-eyes-promotion: false
# If enabled, all authorized and unauthorized accesses attempts to secured endpoints will be logged.
access-log: true
# Valid tokens which can be used to authorize calls against the '/api/v1/hooks/**' endpoints.
# webhook-tokens:
# - 'your_hopefully_very_secure_token'

# settings for the agent commands
agent-command:
Expand Down

0 comments on commit ea99ed5

Please sign in to comment.