Skip to content

Commit

Permalink
feat: ability to enforce Configuration Context
Browse files Browse the repository at this point in the history
#ITSJOINLTY-1715
  • Loading branch information
swollner committed Nov 25, 2024
1 parent 2d5ed16 commit d6dfad9
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,13 @@
<constructor-arg value="cache.eduSharingConfigCache"/>
</bean>

<bean name="eduSharingContextCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.eduSharingContextCache"/>
</bean>
<bean name="eduSharingContextCacheByDomain" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.eduSharingContextCacheByDomain"/>
</bean>

<bean name="eduSharingContextCacheById" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.eduSharingContextCacheByDomain"/>
</bean>

<bean name="eduSharingVersionCache" factory-bean="cacheFactory" factory-method="createCache">
<constructor-arg value="cache.eduSharingVersionCache"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
package org.edu_sharing.restservices;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import com.typesafe.config.Config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;

import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.apache.log4j.Logger;
import org.edu_sharing.alfresco.authentication.subsystems.SubsystemChainingAuthenticationService;
import org.edu_sharing.alfresco.lightbend.LightbendConfigLoader;
import org.edu_sharing.alfrescocontext.gate.AlfAppContextGate;
import org.edu_sharing.repository.client.tools.CCConstants;
import org.edu_sharing.repository.server.AuthenticationToolAPI;
import org.edu_sharing.repository.server.authentication.AuthenticationFilter;
import org.edu_sharing.repository.server.authentication.ContextManagementFilter;
import org.edu_sharing.service.authentication.EduAuthentication;
import org.edu_sharing.service.authentication.oauth2.TokenService;
import org.edu_sharing.service.authentication.oauth2.TokenService.Token;
import org.edu_sharing.service.authority.AuthorityServiceFactory;
Expand All @@ -33,16 +21,19 @@
import org.edu_sharing.spring.security.basic.CSRFConfig;
import org.springframework.context.ApplicationContext;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class ApiAuthenticationFilter implements jakarta.servlet.Filter {

Logger logger = Logger.getLogger(ApiAuthenticationFilter.class);

private TokenService tokenService;

@Override
public void destroy() {
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
Expand Down Expand Up @@ -106,42 +97,40 @@ public void doFilter(ServletRequest req, ServletResponse resp,
}
} else if (authHdr.length() > 10 && authHdr.substring(0, 10).equalsIgnoreCase(CCConstants.AUTH_HEADER_EDU_TICKET)) {
String ticket = authHdr.substring(10).trim();
if (ticket != null) {
if (authTool.validateTicket(ticket)) {
// Force a renew of all toolpermissions since they might have now changed!
ToolPermissionServiceFactory.getInstance().getAllAvailableToolPermissions(true);
//if its APIClient username is ignored and is figured out with authentication service
authTool.storeAuthInfoInSession(authTool.getCurrentUser(), ticket, CCConstants.AUTH_TYPE_TICKET, httpReq.getSession());
validatedAuth = authTool.validateAuthentication(session);
}
if (authTool.validateTicket(ticket)) {
// Force a renew of all toolpermissions since they might have now changed!
ToolPermissionServiceFactory.getInstance().getAllAvailableToolPermissions(true);
//if its APIClient username is ignored and is figured out with authentication service
authTool.storeAuthInfoInSession(authTool.getCurrentUser(), ticket, CCConstants.AUTH_TYPE_TICKET, httpReq.getSession());
validatedAuth = authTool.validateAuthentication(session);
}
}

}
Config accessConfig = LightbendConfigLoader.get().getConfig("security.access");
List<String> AUTHLESS_ENDPOINTS = Arrays.asList(new String[]{"/authentication", "/_about", "/config", "/register", "/sharing",
List<String> AUTHLESS_ENDPOINTS = Arrays.asList("/authentication", "/_about", "/config", "/register", "/sharing",
"/lti/v13/oidc/login_initiations",
"/lti/v13/lti13",
"/lti/v13/registration/dynamic",
"/lti/v13/jwks",
"/lti/v13/details",
"/ltiplatform/v13/openid-configuration",
"/ltiplatform/v13/openid-registration",
"/ltiplatform/v13/content"});
List<String> ADMIN_ENDPOINTS = Arrays.asList(new String[]{"/admin", "/bulk", "/lti/v13/registration/static", "/lti/v13/registration/url"});
"/ltiplatform/v13/content");
List<String> ADMIN_ENDPOINTS = Arrays.asList("/admin", "/bulk", "/lti/v13/registration/static", "/lti/v13/registration/url");
List<String> DISABLED_ENDPOINTS = new ArrayList<>();

try {
if (!ConfigServiceFactory.getCurrentConfig(req).getValue("register.local", true)) {
if (ConfigServiceFactory.getCurrentConfig(req).getValue("register.recoverPassword", false)) {
if (!ConfigServiceFactory.getCurrentConfig(httpReq).getValue("register.local", true)) {
if (ConfigServiceFactory.getCurrentConfig(httpReq).getValue("register.recoverPassword", false)) {
DISABLED_ENDPOINTS.add("/register/v1/register");
DISABLED_ENDPOINTS.add("/register/v1/activate");
} else {
// disable whole api range
DISABLED_ENDPOINTS.add("/register");
}
}
} catch (Exception e) {
} catch (Exception ignored) {
}

boolean noAuthenticationNeeded = false;
Expand Down Expand Up @@ -203,13 +192,8 @@ public void doFilter(ServletRequest req, ServletResponse resp,
return;
}

/**
* allow authless calls with AUTH_SINGLE_USE_NODEID by appauth
*/
boolean trustedAuth = false;
if (ContextManagementFilter.accessTool != null && ContextManagementFilter.accessTool.get() != null) {
trustedAuth = true;
}
// allow authless calls with AUTH_SINGLE_USE_NODEID by appauth
boolean trustedAuth = ContextManagementFilter.accessTool != null && ContextManagementFilter.accessTool.get() != null;

// ignore the auth for the login
if (validatedAuth == null && (!noAuthenticationNeeded && !trustedAuth)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
Expand Down Expand Up @@ -44,6 +48,7 @@
import org.edu_sharing.service.admin.model.RepositoryConfig;
import org.edu_sharing.service.admin.model.ServerUpdateInfo;
import org.edu_sharing.service.admin.model.ToolPermission;
import org.edu_sharing.service.config.ConfigServiceFactory;
import org.edu_sharing.service.lifecycle.PersonDeleteOptions;
import org.edu_sharing.service.lifecycle.PersonLifecycleService;
import org.edu_sharing.service.lifecycle.PersonReport;
Expand All @@ -57,10 +62,6 @@
import org.edu_sharing.service.version.RepositoryVersionInfo;
import org.glassfish.jersey.media.multipart.FormDataParam;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.Serializable;
Expand Down Expand Up @@ -1523,6 +1524,47 @@ public Response setConfig(@Context HttpServletRequest req,RepositoryConfig confi
return ErrorResponse.createResponse(t);
}
}

@PUT
@Path("/repositoryConfig/enforceContext/{contextId}")
@Operation(summary = "set/update the repository config object")
@ApiResponses(value = {
@ApiResponse(responseCode="200", description=RestConstants.HTTP_200, content = @Content(schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode="400", description=RestConstants.HTTP_400, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="401", description=RestConstants.HTTP_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="403", description=RestConstants.HTTP_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="404", description=RestConstants.HTTP_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="500", description=RestConstants.HTTP_500, content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
public Response enforceContext(@Context HttpServletRequest req, @PathParam("contextId") String contextId) {
try {
ConfigServiceFactory.enforceContext(contextId);
return Response.ok().build();
} catch (Throwable t) {
return ErrorResponse.createResponse(t);
}
}

@DELETE
@Path("/repositoryConfig/enforceContext")
@Operation(summary = "set/update the repository config object")
@ApiResponses(value = {
@ApiResponse(responseCode="200", description=RestConstants.HTTP_200, content = @Content(schema = @Schema(implementation = Void.class))),
@ApiResponse(responseCode="400", description=RestConstants.HTTP_400, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="401", description=RestConstants.HTTP_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="403", description=RestConstants.HTTP_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="404", description=RestConstants.HTTP_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode="500", description=RestConstants.HTTP_500, content = @Content(schema = @Schema(implementation = ErrorResponse.class))) })
public Response clearEnforcedContext(@Context HttpServletRequest req) {
try {
ConfigServiceFactory.clearEnforcedContext();
return Response.ok().build();
} catch (Throwable t) {
return ErrorResponse.createResponse(t);
}
}



@GET
@Path("/configFile")
@Operation(summary = "get a base system config file (e.g. edu-sharing.conf)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Response getConfig() {
ConfigService configService = ConfigServiceFactory.getConfigService();
config.setGlobal(configService.getConfig().values);
try {
Context context = configService.getContext(ConfigServiceFactory.getCurrentDomain());
Context context = configService.getContextByDomain(ConfigServiceFactory.getCurrentDomain());
if (context != null) {
config.setContextId(context.id);
config.setCurrent(configService.getConfigByContext(context).values);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package org.edu_sharing.service.admin;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.alfresco.service.cmr.repository.NodeRef;
import org.edu_sharing.repository.client.rpc.cache.CacheCluster;
import org.edu_sharing.repository.client.rpc.cache.CacheInfo;
import org.edu_sharing.repository.server.jobs.quartz.ImmediateJobListener;
import org.edu_sharing.repository.server.jobs.quartz.JobDescription;
import org.edu_sharing.repository.server.jobs.quartz.JobInfo;
import org.edu_sharing.repository.server.tools.ApplicationInfo;
import org.edu_sharing.repository.server.tools.PropertiesHelper;
import org.edu_sharing.restservices.admin.v1.model.PluginStatus;
import org.edu_sharing.service.admin.model.GlobalGroup;
import org.edu_sharing.repository.server.jobs.quartz.JobInfo;
import org.edu_sharing.service.admin.model.RepositoryConfig;
import org.edu_sharing.service.admin.model.ServerUpdateInfo;
import org.edu_sharing.service.admin.model.ToolPermission;
import org.edu_sharing.service.version.RepositoryVersionInfo;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public interface AdminService {

List<JobInfo> getJobs() throws Throwable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ public interface ConfigService {
* @return
* @throws Exception
*/
Context getContext(String domain) throws Exception;
Context getContextByDomain(String domain) throws Exception;
List<Context> getAvailableContext() throws Exception;

Context getContextById(String id) throws Exception;

Context createOrUpdateContext(Context context);

Config getConfigByDomain(String domain) throws Exception;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
package org.edu_sharing.service.config;

import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.edu_sharing.repository.server.AuthenticationToolAPI;
import org.edu_sharing.alfresco.repository.server.authentication.Context;
import org.edu_sharing.alfresco.service.config.model.Config;
import org.edu_sharing.alfresco.service.config.model.KeyValuePair;
import org.edu_sharing.alfresco.service.config.model.Language;
import org.edu_sharing.repository.server.AuthenticationToolAPI;
import org.edu_sharing.repository.server.RequestHelper;

import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.edu_sharing.spring.ApplicationContextFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class ConfigServiceFactory {
private static final String[] DEFAULT_LANGUAGES = new String[]{"de", "en"};
private static final String[] DEFAULT_LANGUAGES = new String[]{"de", "en"};
public static final String ENFORCED_CONTEXT = "ENFORCED_CONTEXT";
static Logger logger = Logger.getLogger(ConfigServiceFactory.class);

public static ConfigService getConfigService(){
Expand All @@ -33,7 +35,7 @@ public static String getCurrentContextId(){
}
public static String getCurrentContextId(HttpServletRequest req){
try {
org.edu_sharing.alfresco.service.config.model.Context context = getConfigService().getContext(getCurrentDomain(req));
org.edu_sharing.alfresco.service.config.model.Context context = getConfigService().getContextByDomain(getCurrentDomain(req));
if(context == null) {
return null;
}
Expand All @@ -43,7 +45,7 @@ public static String getCurrentContextId(HttpServletRequest req){
return null;
}
}
public static Config getCurrentConfig(ServletRequest req) throws Exception {
public static Config getCurrentConfig(HttpServletRequest req) throws Exception {
try {
return getConfigService().getConfigByDomain(req==null ? getCurrentDomain() : getCurrentDomain(req));
}catch(Throwable t) {
Expand All @@ -54,12 +56,42 @@ public static Config getCurrentConfig(ServletRequest req) throws Exception {
public static String getCurrentDomain() {
return getCurrentDomain(Context.getCurrentInstance().getRequest());
}
public static String getCurrentDomain(ServletRequest req) {

public static String getCurrentDomain(HttpServletRequest req) {
Object enforcedContext = req.getSession().getAttribute(ENFORCED_CONTEXT);
if(StringUtils.isNotBlank((String)enforcedContext)){
return enforcedContext.toString();
}

String domain = new RequestHelper(req).getServerName();
logger.debug("current domain:" + domain);
return domain;
}

public static void enforceContext(String contextId){
try {
org.edu_sharing.alfresco.service.config.model.Context context = getConfigService().getContextById(contextId);
if(context == null){
throw new IllegalArgumentException(String.format("Context with contextId %s does not exists",contextId));
}

if(context.domain == null || context.domain.length == 0){
throw new IllegalArgumentException(String.format("Context %s doesn't has domains",contextId));
}

Optional<String> domain = Arrays.stream(context.domain).findFirst();
Context.getCurrentInstance().getRequest().getSession().setAttribute(ENFORCED_CONTEXT, domain.get());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}

public static void clearEnforcedContext(){
Context.getCurrentInstance().getRequest().getSession().removeAttribute(ENFORCED_CONTEXT);
}

public static List<KeyValuePair> getLanguageData(List<Language> languages,String language) {
if(languages!=null && languages.size()>0) {
for(org.edu_sharing.alfresco.service.config.model.Language entry : languages) {
Expand Down
Loading

0 comments on commit d6dfad9

Please sign in to comment.