Skip to content
Snippets Groups Projects
Commit 7d1f1078 authored by srosse's avatar srosse
Browse files

OO-672: authorize access to the system based on a list of IPs

parent b0ca37ba
No related branches found
No related tags found
No related merge requests found
...@@ -19,6 +19,10 @@ ...@@ -19,6 +19,10 @@
*/ */
package org.olat.restapi; package org.olat.restapi;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import org.olat.core.configuration.PersistedProperties; import org.olat.core.configuration.PersistedProperties;
import org.olat.core.configuration.PersistedPropertiesChangedEvent; import org.olat.core.configuration.PersistedPropertiesChangedEvent;
import org.olat.core.gui.control.Event; import org.olat.core.gui.control.Event;
...@@ -41,6 +45,7 @@ public class RestModule implements GenericEventListener { ...@@ -41,6 +45,7 @@ public class RestModule implements GenericEventListener {
private Boolean enabled; private Boolean enabled;
private Boolean defaultEnabled; private Boolean defaultEnabled;
private String ipsByPass;
private PersistedProperties persistedProperties; private PersistedProperties persistedProperties;
private String monitoredProbes; private String monitoredProbes;
...@@ -84,6 +89,25 @@ public class RestModule implements GenericEventListener { ...@@ -84,6 +89,25 @@ public class RestModule implements GenericEventListener {
} }
} }
public String getIpsByPass() {
return ipsByPass;
}
public List<String> getIpsWithSystemAccess() {
List<String> ips = new ArrayList<String>();
for(StringTokenizer tokenizer=new StringTokenizer(ipsByPass, ",;|"); tokenizer.hasMoreTokens(); ) {
String token = tokenizer.nextToken();
if(StringHelper.containsNonWhitespace(token)) {
ips.add(token);
}
}
return ips;
}
public void setIpsByPass(String ipsByPass) {
this.ipsByPass = ipsByPass;
}
/** /**
* @return the persisted properties * @return the persisted properties
*/ */
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<constructor-arg index="1" ref="restModule" /> <constructor-arg index="1" ref="restModule" />
</bean> </bean>
</property> </property>
<property name="ipsByPass" value="${restapi.ips.system}"/>
</bean> </bean>
<bean id="org.olat.restapi.security.RestSecurityBean" class="org.olat.restapi.security.RestSecurityBeanImpl"> <bean id="org.olat.restapi.security.RestSecurityBean" class="org.olat.restapi.security.RestSecurityBeanImpl">
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
package org.olat.restapi.security; package org.olat.restapi.security;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.StringTokenizer; import java.util.StringTokenizer;
...@@ -43,9 +45,11 @@ import org.olat.core.gui.UserRequest; ...@@ -43,9 +45,11 @@ import org.olat.core.gui.UserRequest;
import org.olat.core.gui.UserRequestImpl; import org.olat.core.gui.UserRequestImpl;
import org.olat.core.helpers.Settings; import org.olat.core.helpers.Settings;
import org.olat.core.id.Identity; import org.olat.core.id.Identity;
import org.olat.core.id.Roles;
import org.olat.core.logging.OLog; import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing; import org.olat.core.logging.Tracing;
import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller; import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller;
import org.olat.core.util.SessionInfo;
import org.olat.core.util.StringHelper; import org.olat.core.util.StringHelper;
import org.olat.core.util.UserSession; import org.olat.core.util.UserSession;
import org.olat.core.util.WebappHelper; import org.olat.core.util.WebappHelper;
...@@ -73,6 +77,7 @@ public class RestApiLoginFilter implements Filter { ...@@ -73,6 +77,7 @@ public class RestApiLoginFilter implements Filter {
private static List<String> openUrls; private static List<String> openUrls;
private static List<String> alwaysEnabledUrls; private static List<String> alwaysEnabledUrls;
private static List<String> ipProtectedUrls;
private static String LOGIN_URL; private static String LOGIN_URL;
/** /**
...@@ -121,6 +126,9 @@ public class RestApiLoginFilter implements Filter { ...@@ -121,6 +126,9 @@ public class RestApiLoginFilter implements Filter {
followForAuthentication(requestURI, uress, httpRequest, httpResponse, chain); followForAuthentication(requestURI, uress, httpRequest, httpResponse, chain);
} else if(isRequestURIInOpenSpace(requestURI)) { } else if(isRequestURIInOpenSpace(requestURI)) {
followWithoutAuthentication(httpRequest, httpResponse, chain); followWithoutAuthentication(httpRequest, httpResponse, chain);
} else if( isRequestURIInIPProtectedSpace(requestURI, httpRequest, restModule)) {
upgradeIpAuthentication(httpRequest, httpResponse);
followWithoutAuthentication(httpRequest, httpResponse, chain);
} else if (isRequestTokenValid(httpRequest)) { } else if (isRequestTokenValid(httpRequest)) {
String token = httpRequest.getHeader(RestSecurityHelper.SEC_TOKEN); String token = httpRequest.getHeader(RestSecurityHelper.SEC_TOKEN);
...@@ -211,6 +219,18 @@ public class RestApiLoginFilter implements Filter { ...@@ -211,6 +219,18 @@ public class RestApiLoginFilter implements Filter {
return false; return false;
} }
private boolean isRequestURIInIPProtectedSpace(String requestURI, HttpServletRequest httpRequest, RestModule restModule) {
for(String openURI : getIPProtectedURIs()) {
if(requestURI.startsWith(openURI)) {
String remoteAddr = httpRequest.getRemoteAddr();
if(StringHelper.containsNonWhitespace(remoteAddr)) {
return restModule.getIpsWithSystemAccess().contains(remoteAddr);
}
}
}
return false;
}
private boolean isRequestURIAlwaysEnabled(String requestURI) { private boolean isRequestURIAlwaysEnabled(String requestURI) {
for(String openURI : getAlwaysEnabledURIs()) { for(String openURI : getAlwaysEnabledURIs()) {
if(requestURI.startsWith(openURI)) { if(requestURI.startsWith(openURI)) {
...@@ -271,6 +291,50 @@ public class RestApiLoginFilter implements Filter { ...@@ -271,6 +291,50 @@ public class RestApiLoginFilter implements Filter {
chain.doFilter(request, response); chain.doFilter(request, response);
} }
private void upgradeIpAuthentication(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
UserSessionManager sessionManager = CoreSpringFactory.getImpl(UserSessionManager.class);
UserSession usess = sessionManager.getUserSessionIfAlreadySet(request);
if(usess == null) {
usess = sessionManager.getUserSession(request.getSession(true));
}
if(usess.getIdentity() == null) {
usess.setRoles(new Roles(false, false, false, false, false, false, false));
String remoteAddr = request.getRemoteAddr();
SessionInfo sinfo = new SessionInfo(new Long(-1), "REST", request.getSession());
sinfo.setFirstname("REST");
sinfo.setLastname(remoteAddr);
sinfo.setFromIP(remoteAddr);
sinfo.setFromFQN(remoteAddr);
try {
InetAddress[] iaddr = InetAddress.getAllByName(request.getRemoteAddr());
if (iaddr.length > 0) sinfo.setFromFQN(iaddr[0].getHostName());
} catch (UnknownHostException e) {
// ok, already set IP as FQDN
}
sinfo.setAuthProvider("IP");
sinfo.setUserAgent(request.getHeader("User-Agent"));
sinfo.setSecure(request.isSecure());
sinfo.setREST(true);
sinfo.setWebModeFromUreq(null);
// set session info for this session
usess.setSessionInfo(sinfo);
}
UserRequest ureq = null;
try{
//upon creation URL is checked for
String requestURI = request.getRequestURI();
ureq = new UserRequestImpl(requestURI, request, response);
ureq.getUserSession().putEntryInNonClearedStore(RestSecurityHelper.SYSTEM_MARKER, Boolean.TRUE);
} catch(NumberFormatException nfe) {
response.sendError(401);
return;
}
request.setAttribute(RestSecurityHelper.SEC_USER_REQUEST, ureq);
}
private void followToken(String token, HttpServletRequest request, HttpServletResponse response, FilterChain chain) private void followToken(String token, HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
HttpSession session = request.getSession(true); HttpSession session = request.getSession(true);
...@@ -358,4 +422,13 @@ public class RestApiLoginFilter implements Filter { ...@@ -358,4 +422,13 @@ public class RestApiLoginFilter implements Filter {
} }
return openUrls; return openUrls;
} }
private List<String> getIPProtectedURIs() {
if(ipProtectedUrls == null) {
String context = (Settings.isJUnitTest() ? "/olat" : WebappHelper.getServletContextPath() + RestSecurityHelper.SUB_CONTEXT);
ipProtectedUrls = new ArrayList<String>();
ipProtectedUrls.add(context + "/system");
}
return ipProtectedUrls;
}
} }
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package org.olat.restapi.security; package org.olat.restapi.security;
import java.util.Locale; import java.util.Locale;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
...@@ -51,6 +52,8 @@ public class RestSecurityHelper { ...@@ -51,6 +52,8 @@ public class RestSecurityHelper {
public static final String SEC_TOKEN = "X-OLAT-TOKEN"; public static final String SEC_TOKEN = "X-OLAT-TOKEN";
public static final String SEC_USER_REQUEST = "olat-user-request"; public static final String SEC_USER_REQUEST = "olat-user-request";
protected static final String SYSTEM_MARKER = UUID.randomUUID().toString();
public static UserRequest getUserRequest(HttpServletRequest request) { public static UserRequest getUserRequest(HttpServletRequest request) {
return (UserRequest)request.getAttribute(SEC_USER_REQUEST); return (UserRequest)request.getAttribute(SEC_USER_REQUEST);
...@@ -147,6 +150,23 @@ public class RestSecurityHelper { ...@@ -147,6 +150,23 @@ public class RestSecurityHelper {
} }
} }
public static boolean isAdminOrSystem(HttpServletRequest request) {
try {
Roles roles = getRoles(request);
if(roles.isOLATAdmin()) {
return true;
}
UserRequest ureq = (UserRequest)request.getAttribute(SEC_USER_REQUEST);
if(ureq != null && ureq.getUserSession() != null
&& ureq.getUserSession().getEntry(SYSTEM_MARKER) != null) {
return true;
}
return false;
} catch (Exception e) {
return false;
}
}
public static Roles getRoles(HttpServletRequest request) { public static Roles getRoles(HttpServletRequest request) {
UserRequest ureq= (UserRequest)request.getAttribute(SEC_USER_REQUEST); UserRequest ureq= (UserRequest)request.getAttribute(SEC_USER_REQUEST);
if(ureq == null || ureq.getUserSession() == null || ureq.getUserSession().getRoles() == null) { if(ureq == null || ureq.getUserSession() == null || ureq.getUserSession().getRoles() == null) {
......
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
*/ */
package org.olat.restapi.system; package org.olat.restapi.system;
import static org.olat.restapi.security.RestSecurityHelper.isAdmin;
import java.io.InputStream; import java.io.InputStream;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
...@@ -79,12 +77,6 @@ public class LogWebService { ...@@ -79,12 +77,6 @@ public class LogWebService {
@Path("{date}") @Path("{date}")
@Produces({ "text/plain", MediaType.APPLICATION_OCTET_STREAM }) @Produces({ "text/plain", MediaType.APPLICATION_OCTET_STREAM })
public Response getLogFileByDate(@PathParam("date") String dateString, @Context HttpServletRequest request) { public Response getLogFileByDate(@PathParam("date") String dateString, @Context HttpServletRequest request) {
// logfile download only allowed for admins!
if (!isAdmin(request)) {
return Response.serverError().status(Status.UNAUTHORIZED).build();
}
VFSLeaf logFile; VFSLeaf logFile;
try { try {
logFile = logFileFromParam(dateString); logFile = logFileFromParam(dateString);
...@@ -95,11 +87,11 @@ public class LogWebService { ...@@ -95,11 +87,11 @@ public class LogWebService {
return Response.serverError().status(Status.NOT_FOUND).build(); return Response.serverError().status(Status.NOT_FOUND).build();
InputStream is = logFile.getInputStream(); InputStream is = logFile.getInputStream();
if (is == null) if (is == null) {
return Response.serverError().status(Status.NOT_FOUND).build(); return Response.serverError().status(Status.NOT_FOUND).build();
}
return Response.ok(is).cacheControl(cc).build(); // success return Response.ok(is).cacheControl(cc).build(); // success
} }
@GET @GET
......
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
*/ */
package org.olat.restapi.system; package org.olat.restapi.system;
import static org.olat.restapi.security.RestSecurityHelper.isAdmin;
import java.lang.management.ClassLoadingMXBean; import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean; import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
...@@ -71,10 +69,6 @@ public class RuntimeWebService { ...@@ -71,10 +69,6 @@ public class RuntimeWebService {
@GET @GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getSystemSummaryVO(@Context HttpServletRequest request) { public Response getSystemSummaryVO(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
RuntimeStatisticsVO stats = new RuntimeStatisticsVO(); RuntimeStatisticsVO stats = new RuntimeStatisticsVO();
stats.setMemory(getMemoryStatistics()); stats.setMemory(getMemoryStatistics());
stats.setThreads(getThreadStatistics()); stats.setThreads(getThreadStatistics());
...@@ -104,9 +98,6 @@ public class RuntimeWebService { ...@@ -104,9 +98,6 @@ public class RuntimeWebService {
@Path("memory") @Path("memory")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getMemoryStatistics(@Context HttpServletRequest request) { public Response getMemoryStatistics(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
MemoryStatisticsVO stats = getMemoryStatistics(); MemoryStatisticsVO stats = getMemoryStatistics();
return Response.ok(stats).build(); return Response.ok(stats).build();
} }
...@@ -126,9 +117,6 @@ public class RuntimeWebService { ...@@ -126,9 +117,6 @@ public class RuntimeWebService {
@Path("threads") @Path("threads")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getThreadStatistics(@Context HttpServletRequest request) { public Response getThreadStatistics(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
ThreadStatisticsVO stats = getThreadStatistics(); ThreadStatisticsVO stats = getThreadStatistics();
return Response.ok(stats).build(); return Response.ok(stats).build();
} }
...@@ -147,9 +135,6 @@ public class RuntimeWebService { ...@@ -147,9 +135,6 @@ public class RuntimeWebService {
@Path("classes") @Path("classes")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getCompilationXml(@Context HttpServletRequest request) { public Response getCompilationXml(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
ClasseStatisticsVO stats = getClasseStatistics(); ClasseStatisticsVO stats = getClasseStatistics();
return Response.ok(stats).build(); return Response.ok(stats).build();
} }
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
*/ */
package org.olat.restapi.system; package org.olat.restapi.system;
import static org.olat.restapi.security.RestSecurityHelper.isAdmin; import static org.olat.restapi.security.RestSecurityHelper.isAdminOrSystem;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean; import java.lang.management.OperatingSystemMXBean;
...@@ -55,7 +55,7 @@ public class SystemWebService { ...@@ -55,7 +55,7 @@ public class SystemWebService {
@Path("log") @Path("log")
public LogWebService getLogsWS(@Context HttpServletRequest request) { public LogWebService getLogsWS(@Context HttpServletRequest request) {
if(!isAdmin(request)) { if(!isAdminOrSystem(request)) {
return null; return null;
} }
return new LogWebService(); return new LogWebService();
...@@ -75,7 +75,7 @@ public class SystemWebService { ...@@ -75,7 +75,7 @@ public class SystemWebService {
@Path("environment") @Path("environment")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getEnvironnementXml(@Context HttpServletRequest request) { public Response getEnvironnementXml(@Context HttpServletRequest request) {
if(!isAdmin(request)) { if(!isAdminOrSystem(request)) {
return null; return null;
} }
...@@ -99,7 +99,7 @@ public class SystemWebService { ...@@ -99,7 +99,7 @@ public class SystemWebService {
@Path("release") @Path("release")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getReleaseInfos(@Context HttpServletRequest request) { public Response getReleaseInfos(@Context HttpServletRequest request) {
if(!isAdmin(request)) { if(!isAdminOrSystem(request)) {
return null; return null;
} }
...@@ -112,7 +112,7 @@ public class SystemWebService { ...@@ -112,7 +112,7 @@ public class SystemWebService {
@Path("monitoring") @Path("monitoring")
public MonitoringWebService getImplementedProbes(@Context HttpServletRequest request) { public MonitoringWebService getImplementedProbes(@Context HttpServletRequest request) {
if(!isMonitoringEnabled() && !isAdmin(request)) { if(!isMonitoringEnabled() && !isAdminOrSystem(request)) {
return null; return null;
} }
return new MonitoringWebService(); return new MonitoringWebService();
...@@ -120,7 +120,7 @@ public class SystemWebService { ...@@ -120,7 +120,7 @@ public class SystemWebService {
@Path("indexer") @Path("indexer")
public IndexerWebService getIndexer(@Context HttpServletRequest request) { public IndexerWebService getIndexer(@Context HttpServletRequest request) {
if(!isAdmin(request)) { if(!isAdminOrSystem(request)) {
return null; return null;
} }
return new IndexerWebService(); return new IndexerWebService();
...@@ -128,7 +128,7 @@ public class SystemWebService { ...@@ -128,7 +128,7 @@ public class SystemWebService {
@Path("notifications") @Path("notifications")
public NotificationsAdminWebService getNotifications(@Context HttpServletRequest request) { public NotificationsAdminWebService getNotifications(@Context HttpServletRequest request) {
if(!isAdmin(request)) { if(!isAdminOrSystem(request)) {
return null; return null;
} }
return new NotificationsAdminWebService(); return new NotificationsAdminWebService();
......
...@@ -624,6 +624,11 @@ search.indexing.cronjob.expression=0 0 3 * * ? ...@@ -624,6 +624,11 @@ search.indexing.cronjob.expression=0 0 3 * * ?
restapi.enable=false restapi.enable=false
restapi.enable.values=true,values restapi.enable.values=true,values
#Access to the /restapi/system without authentication if the IP
# of the client is in the list of IPs separated by comma
restapi.ips.system=
restapi.ips.system.values=192.168.1.200,192.168.1.201
######################################################################## ########################################################################
# Security # Security
######################################################################## ########################################################################
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment