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 @@
*/
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.PersistedPropertiesChangedEvent;
import org.olat.core.gui.control.Event;
......@@ -41,6 +45,7 @@ public class RestModule implements GenericEventListener {
private Boolean enabled;
private Boolean defaultEnabled;
private String ipsByPass;
private PersistedProperties persistedProperties;
private String monitoredProbes;
......@@ -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
*/
......
......@@ -14,6 +14,7 @@
<constructor-arg index="1" ref="restModule" />
</bean>
</property>
<property name="ipsByPass" value="${restapi.ips.system}"/>
</bean>
<bean id="org.olat.restapi.security.RestSecurityBean" class="org.olat.restapi.security.RestSecurityBeanImpl">
......
......@@ -20,6 +20,8 @@
package org.olat.restapi.security;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
......@@ -43,9 +45,11 @@ import org.olat.core.gui.UserRequest;
import org.olat.core.gui.UserRequestImpl;
import org.olat.core.helpers.Settings;
import org.olat.core.id.Identity;
import org.olat.core.id.Roles;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.logging.activity.ThreadLocalUserActivityLoggerInstaller;
import org.olat.core.util.SessionInfo;
import org.olat.core.util.StringHelper;
import org.olat.core.util.UserSession;
import org.olat.core.util.WebappHelper;
......@@ -73,6 +77,7 @@ public class RestApiLoginFilter implements Filter {
private static List<String> openUrls;
private static List<String> alwaysEnabledUrls;
private static List<String> ipProtectedUrls;
private static String LOGIN_URL;
/**
......@@ -121,6 +126,9 @@ public class RestApiLoginFilter implements Filter {
followForAuthentication(requestURI, uress, httpRequest, httpResponse, chain);
} else if(isRequestURIInOpenSpace(requestURI)) {
followWithoutAuthentication(httpRequest, httpResponse, chain);
} else if( isRequestURIInIPProtectedSpace(requestURI, httpRequest, restModule)) {
upgradeIpAuthentication(httpRequest, httpResponse);
followWithoutAuthentication(httpRequest, httpResponse, chain);
} else if (isRequestTokenValid(httpRequest)) {
String token = httpRequest.getHeader(RestSecurityHelper.SEC_TOKEN);
......@@ -211,6 +219,18 @@ public class RestApiLoginFilter implements Filter {
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) {
for(String openURI : getAlwaysEnabledURIs()) {
if(requestURI.startsWith(openURI)) {
......@@ -271,6 +291,50 @@ public class RestApiLoginFilter implements Filter {
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)
throws IOException, ServletException {
HttpSession session = request.getSession(true);
......@@ -358,4 +422,13 @@ public class RestApiLoginFilter implements Filter {
}
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 @@
package org.olat.restapi.security;
import java.util.Locale;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
......@@ -51,6 +52,8 @@ public class RestSecurityHelper {
public static final String SEC_TOKEN = "X-OLAT-TOKEN";
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) {
return (UserRequest)request.getAttribute(SEC_USER_REQUEST);
......@@ -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) {
UserRequest ureq= (UserRequest)request.getAttribute(SEC_USER_REQUEST);
if(ureq == null || ureq.getUserSession() == null || ureq.getUserSession().getRoles() == null) {
......
......@@ -19,8 +19,6 @@
*/
package org.olat.restapi.system;
import static org.olat.restapi.security.RestSecurityHelper.isAdmin;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
......@@ -79,12 +77,6 @@ public class LogWebService {
@Path("{date}")
@Produces({ "text/plain", MediaType.APPLICATION_OCTET_STREAM })
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;
try {
logFile = logFileFromParam(dateString);
......@@ -95,11 +87,11 @@ public class LogWebService {
return Response.serverError().status(Status.NOT_FOUND).build();
InputStream is = logFile.getInputStream();
if (is == null)
if (is == null) {
return Response.serverError().status(Status.NOT_FOUND).build();
}
return Response.ok(is).cacheControl(cc).build(); // success
}
@GET
......
......@@ -19,8 +19,6 @@
*/
package org.olat.restapi.system;
import static org.olat.restapi.security.RestSecurityHelper.isAdmin;
import java.lang.management.ClassLoadingMXBean;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
......@@ -71,10 +69,6 @@ public class RuntimeWebService {
@GET
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getSystemSummaryVO(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
RuntimeStatisticsVO stats = new RuntimeStatisticsVO();
stats.setMemory(getMemoryStatistics());
stats.setThreads(getThreadStatistics());
......@@ -104,9 +98,6 @@ public class RuntimeWebService {
@Path("memory")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getMemoryStatistics(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
MemoryStatisticsVO stats = getMemoryStatistics();
return Response.ok(stats).build();
}
......@@ -126,9 +117,6 @@ public class RuntimeWebService {
@Path("threads")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getThreadStatistics(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
ThreadStatisticsVO stats = getThreadStatistics();
return Response.ok(stats).build();
}
......@@ -147,9 +135,6 @@ public class RuntimeWebService {
@Path("classes")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getCompilationXml(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
return null;
}
ClasseStatisticsVO stats = getClasseStatistics();
return Response.ok(stats).build();
}
......
......@@ -19,7 +19,7 @@
*/
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.OperatingSystemMXBean;
......@@ -55,7 +55,7 @@ public class SystemWebService {
@Path("log")
public LogWebService getLogsWS(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
if(!isAdminOrSystem(request)) {
return null;
}
return new LogWebService();
......@@ -75,7 +75,7 @@ public class SystemWebService {
@Path("environment")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getEnvironnementXml(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
if(!isAdminOrSystem(request)) {
return null;
}
......@@ -99,7 +99,7 @@ public class SystemWebService {
@Path("release")
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getReleaseInfos(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
if(!isAdminOrSystem(request)) {
return null;
}
......@@ -112,7 +112,7 @@ public class SystemWebService {
@Path("monitoring")
public MonitoringWebService getImplementedProbes(@Context HttpServletRequest request) {
if(!isMonitoringEnabled() && !isAdmin(request)) {
if(!isMonitoringEnabled() && !isAdminOrSystem(request)) {
return null;
}
return new MonitoringWebService();
......@@ -120,7 +120,7 @@ public class SystemWebService {
@Path("indexer")
public IndexerWebService getIndexer(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
if(!isAdminOrSystem(request)) {
return null;
}
return new IndexerWebService();
......@@ -128,7 +128,7 @@ public class SystemWebService {
@Path("notifications")
public NotificationsAdminWebService getNotifications(@Context HttpServletRequest request) {
if(!isAdmin(request)) {
if(!isAdminOrSystem(request)) {
return null;
}
return new NotificationsAdminWebService();
......
......@@ -624,6 +624,11 @@ search.indexing.cronjob.expression=0 0 3 * * ?
restapi.enable=false
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
########################################################################
......
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