Create IP range helper class
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 5 Aug 2009 12:21:59 +0000 (12:21 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Wed, 5 Aug 2009 12:21:59 +0000 (12:21 +0000)
Refactor IP address login handler to use IP range helper
Add IP authentication for new status handler

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/branches/REL_2@2876 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/main/java/edu/internet2/middleware/shibboleth/idp/StatusServlet.java
src/main/java/edu/internet2/middleware/shibboleth/idp/authn/provider/IPAddressLoginHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/config/profile/authn/AbstractLoginHandlerFactoryBean.java
src/main/java/edu/internet2/middleware/shibboleth/idp/config/profile/authn/IPAddressLoginHandlerBeanDefinitionParser.java
src/main/java/edu/internet2/middleware/shibboleth/idp/config/profile/authn/IPAddressLoginHandlerFactoryBean.java
src/main/java/edu/internet2/middleware/shibboleth/idp/profile/StatusProfileHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/util/IPRange.java [new file with mode: 0644]

index a63cdfb..9b3afe5 100644 (file)
@@ -18,6 +18,8 @@ package edu.internet2.middleware.shibboleth.idp;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
@@ -25,24 +27,36 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.httpclient.HttpStatus;
 import org.joda.time.DateTime;
 import org.joda.time.chrono.ISOChronology;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 import org.opensaml.xml.security.x509.X509Credential;
 import org.opensaml.xml.util.Base64;
+import org.opensaml.xml.util.DatatypeHelper;
+import org.opensaml.xml.util.LazyList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
 import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
 import edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
 
 /** A Servlet for displaying the status of the IdP. */
 public class StatusServlet extends HttpServlet {
 
     /** Serial version UID. */
-    private static final long serialVersionUID = 7917509317276109266L;
+    private static final long serialVersionUID = -5280549109235107879L;
+
+    private final String IP_PARAM_NAME = "AllowedIPs";
+    
+    private final Logger log = LoggerFactory.getLogger(StatusServlet.class);
+
+    private LazyList<IPRange> allowedIPs;
 
     /** Formatter used when print date/times. */
     private DateTimeFormatter dateFormat;
@@ -60,6 +74,15 @@ public class StatusServlet extends HttpServlet {
     public void init(ServletConfig config) throws ServletException {
         super.init(config);
 
+        allowedIPs = new LazyList<IPRange>();
+
+        String cidrBlocks = DatatypeHelper.safeTrimOrNullString(config.getInitParameter(IP_PARAM_NAME));
+        if (cidrBlocks != null) {
+            for (String cidrBlock : cidrBlocks.split(" ")) {
+                allowedIPs.add(IPRange.parseCIDRBlock(cidrBlock));
+            }
+        }
+
         dateFormat = ISODateTimeFormat.dateTimeNoMillis();
         startTime = new DateTime(ISOChronology.getInstanceUTC());
         attributeResolver = HttpServletHelper.getAttributeResolver(config.getServletContext());
@@ -67,20 +90,49 @@ public class StatusServlet extends HttpServlet {
     }
 
     /** {@inheritDoc} */
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
-        resp.setContentType("text/plain");
-        PrintWriter output = resp.getWriter();
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        if (!isAuthenticated(request)) {
+            response.sendError(HttpStatus.SC_UNAUTHORIZED);
+            return;
+        }
+
+        response.setContentType("text/plain");
+        PrintWriter output = response.getWriter();
 
         printOperatingEnvironmentInformation(output);
         output.println();
         printIdPInformation(output);
         output.println();
-        printRelyingPartyConfigurationsInformation(output, req.getParameter("relyingParty"));
+        printRelyingPartyConfigurationsInformation(output, request.getParameter("relyingParty"));
 
         output.flush();
     }
 
     /**
+     * Checks whether the client is authenticated.
+     * 
+     * @param request client request
+     * 
+     * @return true if the client is authenticated, false if not
+     */
+    protected boolean isAuthenticated(HttpServletRequest request) throws ServletException {
+        log.debug("Attempting to authenticate client '{}'", request.getRemoteAddr());
+        try {
+            InetAddress clientAddress = InetAddress.getByName(request.getRemoteAddr());
+
+            for (IPRange range : allowedIPs) {
+                if (range.contains(clientAddress)) {
+                    return true;
+                }
+            }
+
+            return false;
+        } catch (UnknownHostException e) {
+            throw new ServletException(e);
+        }
+    }
+
+    /**
      * Prints out information about the operating environment. This includes the operating system name, version and
      * architecture, the JDK version, available CPU cores, memory currently used by the JVM process, the maximum amount
      * of memory that may be used by the JVM, and the current time in UTC.
@@ -155,20 +207,23 @@ public class StatusServlet extends HttpServlet {
     protected void printRelyingPartyConfigurationInformation(PrintWriter out, RelyingPartyConfiguration config) {
         out.println("relying_party_id: " + config.getRelyingPartyId());
         out.println("idp_entity_id: " + config.getProviderId());
-        
+
         if (config.getDefaultAuthenticationMethod() != null) {
             out.println("default_authentication_method: " + config.getDefaultAuthenticationMethod());
         } else {
             out.println("default_authentication_method: none");
         }
 
-        try{
+        try {
             X509Credential signingCredential = (X509Credential) config.getDefaultSigningCredential();
-            out.println("default_signing_tls_key: " + Base64.encodeBytes(signingCredential.getEntityCertificate().getEncoded(), Base64.DONT_BREAK_LINES));
-        }catch(Throwable t){
+            out
+                    .println("default_signing_tls_key: "
+                            + Base64.encodeBytes(signingCredential.getEntityCertificate().getEncoded(),
+                                    Base64.DONT_BREAK_LINES));
+        } catch (Throwable t) {
             // swallow error
         }
-        
+
         for (String profileId : config.getProfileConfigurations().keySet()) {
             out.println("configured_communication_profile: " + profileId);
         }
index d6f316e..6e02809 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package edu.internet2.middleware.shibboleth.idp.authn.provider;
 
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.BitSet;
+import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 
-import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.opensaml.xml.util.DatatypeHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.slf4j.helpers.MessageFormatter;
 
 import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
+import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationException;
 import edu.internet2.middleware.shibboleth.idp.authn.LoginHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
 
 /**
  * IP Address authentication handler.
  * 
  * This "authenticates" a user based on their IP address. It operates in either default deny or default allow mode, and
  * evaluates a given request against a list of blocked or permitted IPs. It supports both IPv4 and IPv6.
- * 
- * If an Authentication Context Class or DeclRef URI is not specified, it will default to
- * "urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol".
  */
 public class IPAddressLoginHandler extends AbstractLoginHandler {
 
@@ -49,37 +46,26 @@ public class IPAddressLoginHandler extends AbstractLoginHandler {
     private final Logger log = LoggerFactory.getLogger(IPAddressLoginHandler.class);
 
     /** The username to use for IP-address "authenticated" users. */
-    private String username;
-
-    /** Are the IPs in ipList a permitted list or a deny list. */
-    private boolean defaultDeny;
+    private String authenticatedUser;
 
-    /** The list of denied or permitted IPs. */
-    private List<IPEntry> ipList;
+    /** List of configured IP ranged. */
+    private List<IPRange> ipRanges;
 
-    /**
-     * Set the permitted IP addresses.
-     * 
-     * If <code>defaultDeny</code> is <code>true</code> then only the IP addresses in <code>ipList</code> will be
-     * "authenticated." If <code>defaultDeny</code> is <code>false</code>, then all IP addresses except those in
-     * <code>ipList</code> will be authenticated.
-     * 
-     * @param entries A list of IP addresses (with CIDR masks).
-     * @param defaultDeny Does <code>ipList</code> contain a deny or permit list.
-     */
-    public void setEntries(final List<String> entries, boolean defaultDeny) {
+    /** Whether a user is "authenticated" if their IP address is within a configured IP range. */
+    private boolean ipInRangeIsAuthenticated;
 
-        this.defaultDeny = defaultDeny;
-        ipList = new CopyOnWriteArrayList<IPEntry>();
+    public IPAddressLoginHandler(String user, List<IPRange> ranges, boolean ipInRangeIsAuthenticated) {
+        authenticatedUser = DatatypeHelper.safeTrimOrNullString(user);
+        if (authenticatedUser == null) {
+            throw new IllegalArgumentException("The authenticated user ID may not be null or empty");
+        }
 
-        for (String addr : entries) {
-            try {
-                ipList.add(new edu.internet2.middleware.shibboleth.idp.authn.provider.IPAddressLoginHandler.IPEntry(
-                        addr));
-            } catch (UnknownHostException ex) {
-                log.warn("IPAddressHandler: Error parsing IP entry \"" + addr + "\". Ignoring.");
-            }
+        if (ranges == null || ranges.isEmpty()) {
+            throw new IllegalArgumentException("The list of IP ranges may not be null or empty");
         }
+        ipRanges = new ArrayList<IPRange>(ranges);
+
+        this.ipInRangeIsAuthenticated = ipInRangeIsAuthenticated;
     }
 
     /** {@inheritDoc} */
@@ -92,194 +78,51 @@ public class IPAddressLoginHandler extends AbstractLoginHandler {
         return true;
     }
 
-    /**
-     * Get the username for all IP-address authenticated users.
-     * 
-     * @return The username for IP-address authenticated users.
-     */
-    public String getUsername() {
-        return username;
-    }
-
-    /**
-     * Set the username to use for all IP-address authenticated users.
-     * 
-     * @param name The username for IP-address authenticated users.
-     */
-    public void setUsername(String name) {
-        username = name;
-    }
-
     /** {@inheritDoc} */
     public void login(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-
-        if (defaultDeny) {
-            handleDefaultDeny(httpRequest, httpResponse);
-        } else {
-            handleDefaultAllow(httpRequest, httpResponse);
-        }
-
-        AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
-    }
-
-    protected void handleDefaultDeny(HttpServletRequest request, HttpServletResponse response) {
-
-        boolean ipAllowed = searchIpList(request);
-
-        if (ipAllowed) {
-            log.debug("Authenticated user by IP address");
-            request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
-        }
-    }
-
-    protected void handleDefaultAllow(HttpServletRequest request, HttpServletResponse response) {
-
-        boolean ipDenied = searchIpList(request);
-
-        if (!ipDenied) {
-            log.debug("Authenticated user by IP address");
-            request.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, username);
-        }
-    }
-
-    /**
-     * Search the list of InetAddresses for the client's address.
-     * 
-     * @param request The ServletReqeust
-     * 
-     * @return <code>true</code> if the client's address is in <code>ipList</code>
-     */
-    private boolean searchIpList(ServletRequest request) {
-
-        boolean found = false;
-
+        log.debug("Attempting to authenticated client '{}'", httpRequest.getRemoteAddr());
         try {
-            InetAddress addr = InetAddress.getByName(request.getRemoteAddr());
-            BitSet addrbits = byteArrayToBitSet(addr.getAddress());
-
-            for (IPEntry entry : ipList) {
-
-                BitSet netaddr = entry.getNetworkAddress();
-                BitSet netmask = entry.getNetmask();
-
-                addrbits.and(netmask);
-                if (addrbits.equals(netaddr)) {
-                    found = true;
-                    break;
-                }
+            InetAddress clientAddress = InetAddress.getByName(httpRequest.getRemoteAddr());
+            if (authenticate(clientAddress)) {
+                log.debug("Authenticated user by IP address");
+                httpRequest.setAttribute(LoginHandler.PRINCIPAL_NAME_KEY, authenticatedUser);
+            } else {
+                log.debug("Client IP address {} failed authentication.", httpRequest.getRemoteAddr());
+                httpRequest.setAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY, new AuthenticationException(
+                        "Client failed IP address authentication"));
             }
-
-        } catch (UnknownHostException ex) {
-            log.error("Error resolving hostname.", ex);
-            return false;
+        } catch (UnknownHostException e) {
+            String msg = MessageFormatter.format("Unable to resolve {} in to an IP address", httpRequest
+                    .getRemoteAddr());
+            log.warn(msg);
+            httpRequest.setAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY, new AuthenticationException(msg));
         }
 
-        return found;
+        AuthenticationEngine.returnToAuthenticationEngine(httpRequest, httpResponse);
     }
 
     /**
-     * Converts a byte array to a BitSet.
+     * Authenticates the client address.
      * 
-     * The supplied byte array is assumed to have the most signifigant bit in element 0.
+     * @param clientAddress the client address
      * 
-     * @param bytes the byte array with most signifigant bit in element 0.
-     * 
-     * @return the BitSet
-     */
-    protected BitSet byteArrayToBitSet(final byte[] bytes) {
-
-        BitSet bits = new BitSet();
-
-        for (int i = 0; i < bytes.length * 8; i++) {
-            if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
-                bits.set(i);
-            }
-        }
-
-        return bits;
-    }
-
-    /**
-     * Encapsulates a network address and a netmask on ipList.
+     * @return true if the client address is authenticated, false it not
      */
-    protected class IPEntry {
-
-        /** The network address. */
-        private final BitSet networkAddress;
-
-        /** The netmask. */
-        private final BitSet netmask;
-
-        /**
-         * Construct a new IPEntry given a network address in CIDR format.
-         * 
-         * @param entry A CIDR-formatted network address/netmask
-         * 
-         * @throws UnknownHostException If entry is malformed.
-         */
-        public IPEntry(String entry) throws UnknownHostException {
-
-            // quick sanity checks
-            if (entry == null || entry.length() == 0) {
-                throw new UnknownHostException("entry is null.");
-            }
-
-            int cidrOffset = entry.indexOf("/");
-            if (cidrOffset == -1) {
-                log.warn("Invalid entry \"" + entry + "\" -- it lacks a netmask component.");
-                throw new UnknownHostException("entry lacks a netmask component.");
-            }
-
-            // ensure that only one "/" is present.
-            if (entry.indexOf("/", cidrOffset + 1) != -1) {
-                log.warn("Invalid entry \"" + entry + "\" -- too many \"/\" present.");
-                throw new UnknownHostException("entry has too many netmask components.");
-            }
-
-            String networkString = entry.substring(0, cidrOffset);
-            String netmaskString = entry.substring(cidrOffset + 1, entry.length());
-
-            InetAddress tempAddr = InetAddress.getByName(networkString);
-            networkAddress = byteArrayToBitSet(tempAddr.getAddress());
-
-            int masklen = Integer.parseInt(netmaskString);
-
-            int addrlen;
-            if (tempAddr instanceof Inet4Address) {
-                addrlen = 32;
-            } else if (tempAddr instanceof Inet6Address) {
-                addrlen = 128;
-            }else{
-                throw new UnknownHostException("Unable to determine Inet protocol version");
+    protected boolean authenticate(InetAddress clientAddress) {
+        if (ipInRangeIsAuthenticated) {
+            for (IPRange range : ipRanges) {
+                if (range.contains(clientAddress)) {
+                    return true;
+                }
             }
-
-            // ensure that the netmask isn't too large
-            if ((tempAddr instanceof Inet4Address) && (masklen > 32)) {
-                throw new UnknownHostException("Netmask is too large for an IPv4 address: " + masklen);
-            } else if ((tempAddr instanceof Inet6Address) && masklen > 128) {
-                throw new UnknownHostException("Netmask is too large for an IPv6 address: " + masklen);
+        } else {
+            for (IPRange range : ipRanges) {
+                if (!range.contains(clientAddress)) {
+                    return true;
+                }
             }
-
-            netmask = new BitSet(addrlen);
-            netmask.set(addrlen - masklen, addrlen, true);
-        }
-
-        /**
-         * Get the network address.
-         * 
-         * @return the network address.
-         */
-        public BitSet getNetworkAddress() {
-            return networkAddress;
         }
 
-        /**
-         * Get the netmask.
-         * 
-         * @return the netmask.
-         */
-        public BitSet getNetmask() {
-            return netmask;
-        }
+        return false;
     }
 }
\ No newline at end of file
index d04bb4c..da37c04 100644 (file)
@@ -78,6 +78,6 @@ public abstract class AbstractLoginHandlerFactoryBean extends AbstractFactoryBea
         if (authenticationMethods != null) {
             handler.getSupportedAuthenticationMethods().addAll(authenticationMethods);
         }
-        handler.setAuthenticationDurection(authenticationDuration * 60 * 1000);
+        handler.setAuthenticationDuration(authenticationDuration * 60 * 1000);
     }
 }
index c3dc07d..e9ef96f 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.config.profile.authn;
 
-import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import javax.xml.namespace.QName;
 
 import org.opensaml.xml.util.DatatypeHelper;
+import org.opensaml.xml.util.LazyList;
 import org.opensaml.xml.util.XMLHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
 import org.w3c.dom.Element;
 
 import edu.internet2.middleware.shibboleth.idp.config.profile.ProfileHandlerNamespaceHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
 
-/**
- * Spring bean definition parser for IP address authentication handlers.
- */
+/** Spring bean definition parser for IP address authentication handlers. */
 public class IPAddressLoginHandlerBeanDefinitionParser extends AbstractLoginHandlerBeanDefinitionParser {
 
     /** Schema type. */
     public static final QName SCHEMA_TYPE = new QName(ProfileHandlerNamespaceHandler.NAMESPACE, "IPAddress");
 
-    /** Name of ip entry elements. */
-    public static final QName IP_ENTRY_ELEMENT_NAME = new QName(ProfileHandlerNamespaceHandler.NAMESPACE, "IPEntry");
-
     /** Class logger. */
     private final Logger log = LoggerFactory.getLogger(IPAddressLoginHandlerBeanDefinitionParser.class);
 
@@ -54,25 +50,45 @@ public class IPAddressLoginHandlerBeanDefinitionParser extends AbstractLoginHand
     protected void doParse(Element config, BeanDefinitionBuilder builder) {
         super.doParse(config, builder);
 
+        String username = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "username"));
+        if (username == null) {
+            String msg = "No username specified.";
+            log.error(msg);
+            throw new BeanCreationException(msg);
+        }
+        log.debug("authenticated username: {}", username);
+        builder.addPropertyValue("authenticatedUser", username);
+
+        List<IPRange> ranges = getIPRanges(config);
+        log.debug("registered IP ranges: {}", ranges.size());
+        builder.addPropertyValue("ipRanges", ranges);
+
         boolean defaultDeny = XMLHelper.getAttributeValueAsBoolean(config.getAttributeNodeNS(null, "defaultDeny"));
-        log.debug("Setting defaultDeny to: {}", defaultDeny);
-        builder.addPropertyValue("defaultDeny", defaultDeny);
-
-        String username = DatatypeHelper.safeTrim(config.getAttributeNS(null, "username"));
-        log.debug("Setting username to: {}", username);
-        builder.addPropertyValue("username", username);
-
-        Map<QName, List<Element>> children = XMLHelper.getChildElements(config);
-        List<Element> ipEntries = children.get(IP_ENTRY_ELEMENT_NAME);
-        List<String> addresses = new ArrayList<String>();
-
-        for (Element element : ipEntries) {
-            String address = DatatypeHelper.safeTrimOrNullString(element.getTextContent());
-            if (address != null) {
-                log.debug("Adding IP Address: {}", address);
-                addresses.add(address);
-            }
+        log.debug("default deny: {}", defaultDeny);
+        builder.addPropertyValue("ipInRangeIsAuthenticated", defaultDeny);
+    }
+
+    /**
+     * Gets the list of IP ranges given in the configuration.
+     * 
+     * @param config current configuration
+     * 
+     * @return list of IP ranges
+     */
+    protected List<IPRange> getIPRanges(Element config) {
+        List<Element> ipEntries = XMLHelper.getChildElementsByTagNameNS(config,
+                ProfileHandlerNamespaceHandler.NAMESPACE, "IPEntry");
+        if (ipEntries == null || ipEntries.isEmpty()) {
+            String msg = "At least one IPEntry must be specified.";
+            log.error(msg);
+            throw new BeanCreationException(msg);
         }
-        builder.addPropertyValue("addresses", addresses);
+
+        List<IPRange> ranges = new LazyList<IPRange>();
+        for (Element ipEntry : ipEntries) {
+            ranges.add(IPRange.parseCIDRBlock(ipEntry.getTextContent()));
+        }
+
+        return ranges;
     }
-}
+}
\ No newline at end of file
index 27067aa..807e648 100644 (file)
@@ -19,87 +19,52 @@ package edu.internet2.middleware.shibboleth.idp.config.profile.authn;
 import java.util.List;
 
 import edu.internet2.middleware.shibboleth.idp.authn.provider.IPAddressLoginHandler;
+import edu.internet2.middleware.shibboleth.idp.util.IPRange;
 
 /**
  * Spring factory for {@link IPAddressLoginHandler}.
  */
 public class IPAddressLoginHandlerFactoryBean extends AbstractLoginHandlerFactoryBean {
 
-    /** The list of denied or permitted IPs. */
-    private List<String> addresses;
-
     /** The username to use for IP-address "authenticated" users. */
-    private String username;
+    private String authenticatedUser;
 
-    /** Are the IPs in ipList a permitted list or a deny list. */
-    private boolean defaultDeny;
+    /** List of configured IP ranged. */
+    private List<IPRange> ipRanges;
 
-    /** {@inheritDoc} */
-    protected Object createInstance() throws Exception {
-        IPAddressLoginHandler handler = new IPAddressLoginHandler();
-        handler.setUsername(getUsername());
-        handler.setEntries(getAddresses(), isDefaultDeny());
-        populateHandler(handler);
-        return handler;
-    }
+    /** Whether a user is "authenticated" if their IP address is within a configured IP range. */
+    private boolean ipInRangeIsAuthenticated;
 
     /** {@inheritDoc} */
     public Class getObjectType() {
         return IPAddressLoginHandler.class;
     }
-
-    /**
-     * Get the list of denied or permitted IPs.
-     * 
-     * @return list of denied or permitted IPs
-     */
-    public List<String> getAddresses() {
-        return addresses;
-    }
-
-    /**
-     * Set the list of denied or permitted IPs.
-     * 
-     * @param newAddresses list of denied or permitted IPs
-     */
-    public void setAddresses(List<String> newAddresses) {
-        addresses = newAddresses;
-    }
-
+    
     /**
-     * Get the username to use for IP-address "authenticated" users.
-     * 
-     * @return username to use for IP-address "authenticated" users
+     * @param user The authenticatedUser to set.
      */
-    public String getUsername() {
-        return username;
+    public void setAuthenticatedUser(String user) {
+        authenticatedUser = user;
     }
 
     /**
-     * Set the username to use for IP-address "authenticated" users.
-     * 
-     * @param newUsername username to use for IP-address "authenticated" users
+     * @param ranges The ipRanges to set.
      */
-    public void setUsername(String newUsername) {
-        username = newUsername;
+    public void setIpRanges(List<IPRange> ranges) {
+        ipRanges = ranges;
     }
 
     /**
-     * Get whether the IPs in ipList a permitted list or a deny list.
-     * 
-     * @return whether the IPs in ipList a permitted list or a deny list
+     * @param authenticated The ipInRangeIsAuthenticated to set.
      */
-    public boolean isDefaultDeny() {
-        return defaultDeny;
+    public void setIpInRangeIsAuthenticated(boolean authenticated) {
+        ipInRangeIsAuthenticated = authenticated;
     }
 
-    /**
-     * Set whether the IPs in ipList a permitted list or a deny list.
-     * 
-     * @param newDefaultDeny whether the IPs in ipList a permitted list or a deny list.
-     */
-    public void setDefaultDeny(boolean newDefaultDeny) {
-        defaultDeny = newDefaultDeny;
+    /** {@inheritDoc} */
+    protected Object createInstance() throws Exception {
+        IPAddressLoginHandler handler = new IPAddressLoginHandler(authenticatedUser, ipRanges, ipInRangeIsAuthenticated);
+        populateHandler(handler);
+        return handler;
     }
-
-}
+}
\ No newline at end of file
index 2af3fbf..b19ca46 100644 (file)
@@ -29,6 +29,8 @@ import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractReque
 /**
  * A simple profile handler that returns the string "ok" if the IdP is able to answer the request. This may be used for
  * very basic monitoring of the IdP.
+ * 
+ * @deprecated
  */
 public class StatusProfileHandler extends AbstractRequestURIMappedProfileHandler {
 
diff --git a/src/main/java/edu/internet2/middleware/shibboleth/idp/util/IPRange.java b/src/main/java/edu/internet2/middleware/shibboleth/idp/util/IPRange.java
new file mode 100644 (file)
index 0000000..dd0a75b
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2009 University Corporation for Advanced Internet Development, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.idp.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.BitSet;
+
+import org.opensaml.xml.util.DatatypeHelper;
+
+/** Represents a range of IP addresses. */
+public class IPRange {
+
+    /** Number of bits within */
+    private int addressLength;
+
+    /** The IP network address for the range. */
+    private BitSet network;
+
+    /** The netmask for the range. */
+    private BitSet mask;
+    
+    /**
+     * Constructor
+     * 
+     * @param networkAddress the network address for the range
+     * @param maskSize the number of bits in the netmask
+     */
+    public IPRange(InetAddress networkAddress, int maskSize) {
+        this(networkAddress.getAddress(), maskSize);
+    }
+
+    /**
+     * Constructor
+     * 
+     * @param networkAddress the network address for the range
+     * @param maskSize the number of bits in the netmask
+     */
+    public IPRange(byte[] networkAddress, int maskSize) {
+        addressLength = networkAddress.length * 8;
+        if (addressLength != 32 && addressLength != 128) {
+            throw new IllegalArgumentException("Network address was neither an IPv4 or IPv6 address");
+        }
+
+        network = toBitSet(networkAddress);
+        mask = new BitSet(addressLength);
+        mask.set(addressLength - maskSize, addressLength, true);
+    }
+
+    /**
+     * Parses a CIDR block definition in to an IP range.
+     * 
+     * @param cidrBlock the CIDR block definition
+     * 
+     * @return the resultant IP range
+     */
+    public static IPRange parseCIDRBlock(String cidrBlock){
+        String block = DatatypeHelper.safeTrimOrNullString(cidrBlock);
+        if(block == null){
+            throw new IllegalArgumentException("CIDR block definition may not be null");
+        }
+        
+        String[] blockParts = block.split("/");
+        try{
+            InetAddress networkAddress = InetAddress.getByName(blockParts[0]);
+            int maskSize = Integer.parseInt(blockParts[1]);
+            return new IPRange(networkAddress, maskSize);
+        }catch(UnknownHostException e){
+            throw new IllegalArgumentException("Invalid IP address");
+        }catch(NumberFormatException e){
+            throw new IllegalArgumentException("Invalid netmask size");
+        }
+    }
+    
+    /**
+     * Determines whether the given address is contained in the IP range.
+     * 
+     * @param address the address to check
+     * 
+     * @return true if the address is in the range, false it not
+     */
+    public boolean contains(InetAddress address) {
+        return contains(address.getAddress());
+    }
+
+    /**
+     * Determines whether the given address is contained in the IP range.
+     * 
+     * @param address the address to check
+     * 
+     * @return true if the address is in the range, false it not
+     */
+    public boolean contains(byte[] address) {
+        if (address.length * 8 != addressLength) {
+            return false;
+        }
+
+        BitSet addrNetwork = toBitSet(address);
+        addrNetwork.and(mask);
+
+        return addrNetwork.equals(network);
+    }
+
+    /**
+     * Converts a byte array to a BitSet.
+     * 
+     * The supplied byte array is assumed to have the most significant bit in element 0.
+     * 
+     * @param bytes the byte array with most significant bit in element 0.
+     * 
+     * @return the BitSet
+     */
+    protected BitSet toBitSet(byte[] bytes) {
+        BitSet bits = new BitSet(bytes.length * 8);
+
+        for (int i = 0; i < bytes.length * 8; i++) {
+            if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) {
+                bits.set(i);
+            }
+        }
+
+        return bits;
+    }
+}
\ No newline at end of file