Package org.cafesip.jiplet.cma

Source Code of org.cafesip.jiplet.cma.SecurityConstraintManager

/*
* Created on Jun 25, 2005
*
* Copyright 2005 CafeSip.org
*
* 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 org.cafesip.jiplet.cma;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipProvider;
import javax.sip.address.SipURI;
import javax.sip.header.AuthorizationHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.ProxyAuthenticateHeader;
import javax.sip.header.ProxyAuthorizationHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.cafesip.jiplet.Jiplet;
import org.cafesip.jiplet.JipletContainer;
import org.cafesip.jiplet.JipletContext;
import org.cafesip.jiplet.JipletException;
import org.cafesip.jiplet.JipletLogger;
import org.cafesip.jiplet.JipletPrincipal;
import org.cafesip.jiplet.Pair;
import org.cafesip.jiplet.Realm;
import org.cafesip.jiplet.config.jip.AuthConstraint;
import org.cafesip.jiplet.config.jip.JipApplication;
import org.cafesip.jiplet.config.jip.SecurityConstraint;

/**
* @author Amit Chatterjee
*
*/
public class SecurityConstraintManager extends TimerTask
{
    private Authorizations authorizations = new Authorizations();

    private JipletContext context;

    private HashMap jipletMap = new HashMap();

    // key= jiplet, value = Pair(realm, allowed roles)

    private Timer timer = new Timer();

    private static final long DEFAULT_CACHE_PERIOD = 30 * 60 * 1000L; // 30

    // minutes

    private long cachePeriod = DEFAULT_CACHE_PERIOD;

    private boolean authOnLogout = true;

    /**
     * A constructor for this class.
     *
     *
     */
    public SecurityConstraintManager(JipletContext context)
    {
        super();
        this.context = context;
        timer.scheduleAtFixedRate(this, 10000L, 10000L);
    }

    public void init(JipApplication config) throws Exception
    {
        if (JipletContainer.getInstance().getDefaultRealm() == null)
        {
            JipletLogger
                    .info("Realms have not been configured for this system. We will ignore any security constraints");
            return;
        }

        Iterator iter = config.getSecurityConstraint().iterator();
        while (iter.hasNext() == true)
        {
            SecurityConstraint constraint = (SecurityConstraint) iter.next();
            AuthConstraint auth = constraint.getAuthConstraint();
            Iterator i = auth.getRoleName().iterator();
            String[] roles = new String[auth.getRoleName().size()];
            int index = 0;
            while (i.hasNext() == true)
            {
                String role = (String) i.next();
                roles[index++] = role;
            }

            i = constraint.getJipletResourceCollection().getJipletNames()
                    .getJipletName().iterator();
            while (i.hasNext() == true)
            {
                String jiplet = (String) i.next();

                Jiplet j = context.findJiplet(jiplet);
                if (j == null)
                {
                    throw new JipletException(
                            "While creating security constraint for collection "
                                    + constraint.getJipletResourceCollection()
                                            .getJipletResourceName()
                                    + " jiplet " + jiplet + " was not found");
                }

                synchronized (jipletMap)
                {
                    String rname = auth.getRealm();
                    if (rname == null)
                    {
                        rname = JipletContainer.getInstance().getDefaultRealm();
                    }

                    Realm realm = JipletContainer.getInstance()
                            .findRealm(rname);
                    if (realm == null)
                    {
                        throw new JipletException(
                                "While creating security constraint for collection "
                                        + constraint
                                                .getJipletResourceCollection()
                                                .getJipletResourceName()
                                        + " realm " + rname + " was not found");
                    }

                    jipletMap.put(j, new Pair(realm, roles));
                    JipletLogger.info("Mapped security constraint for jiplet "
                            + jiplet + " to realm " + rname);
                }
            }
        }
    }

    public void destroy()
    {
        timer.cancel();

        synchronized (jipletMap)
        {
            jipletMap.clear();
        }
    }

    public Pair applySecurityPolicy(Jiplet jiplet, RequestEvent event)
    {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        Request req = event.getRequest();
        String method = req.getMethod();
        if ((method.equals(Request.ACK) == true)
                || (method.equals(Request.CANCEL) == true))
        {
            // do not send a challenge for ACK and CANCEL messages
            return new Pair(null, null);
        }

        Pair p = null;
        synchronized (jipletMap)
        {
            p = (Pair) jipletMap.get(jiplet);
            if (p == null)
            {
                // the jiplet does not have any security constraint
                return new Pair(null, null);
            }
        }

        Realm realm = (Realm) p.getFirst();
        String[] roles = (String[]) p.getSecond();
        String realm_name = null;
        try
        {
            Thread.currentThread().setContextClassLoader(
                    realm.getClass().getClassLoader());
            realm_name = realm.getRealmName();
        }
        finally
        {
            Thread.currentThread().setContextClassLoader(cl);
        }

        // check if the request message contains an Authorization Header that
        // belongs to this realm

        String header_name = method.equals(Request.REGISTER) == true ? AuthorizationHeader.NAME
                : ProxyAuthorizationHeader.NAME;

        ListIterator iter = req.getHeaders(header_name);
        boolean found = false;
        while (iter.hasNext() == true)
        {
            found = true;
            AuthorizationHeader header = (AuthorizationHeader) iter.next();
            String call_id = ((CallIdHeader) req.getHeader(CallIdHeader.NAME))
                    .getCallId();

            if (JipletLogger.isDebugEnabled() == true)
            {
                JipletLogger.debug("Found an authentication entry for call-id"
                        + call_id + " for realm " + header.getRealm());
            }

            // check if the user has already been authenticated
            String[] uroles = authorizations.findEntry(header.getRealm(),
                    call_id, header.getNonce(), header.getResponse());
            if (uroles != null)
            {
                if (JipletLogger.isDebugEnabled() == true)
                {
                    JipletLogger.debug("Authenticated call-id " + call_id
                            + " from cached authentications");
                }
                // update the cached information with the new time-stamp
                AuthorizationInfo ainfo = new AuthorizationInfo();
                ainfo.setRealm(realm_name);
                ainfo.setResponse(header.getResponse());
                ainfo.setCallId(call_id);
                ainfo.setNonce(header.getNonce());
                authorizations.addEntry(ainfo, new Date());

                // the user has a prior authentication for this realm, check if
                // the user has proper authority
                if (hasAuthorization(roles, uroles) == true)
                {
                    return new Pair(new JipletPrincipal(header.getUsername()),
                            uroles);
                }
                else
                {
                    // send a FORBIDDEN response
                    sendResponse(jiplet, event, Response.FORBIDDEN,
                            "You are not authorized to use this service", null);
                    return null;
                }
            }
            else
            {
                // the user either does not prior authentication or the auth
                // failed. Check if the authentication info that the user has
                // sent can
                // be authenticated.

                try
                {
                    // set the context class loader to that of the realm.
                    Thread.currentThread().setContextClassLoader(
                            realm.getClass().getClassLoader());
                    uroles = realm.authenticate(req.getMethod(), header);
                }
                finally
                {
                    Thread.currentThread().setContextClassLoader(cl);
                }

                if (uroles != null)
                {
                    // add the information to the cached authorizations
                    AuthorizationInfo ainfo = new AuthorizationInfo();
                    ainfo.setRealm(realm_name);
                    ainfo.setResponse(header.getResponse());
                    ainfo.setCallId(call_id);
                    ainfo.setNonce(header.getNonce());
                    authorizations.addEntry(ainfo, new Date());

                    // the user has proper authentication for this realm, check
                    // if the user has proper authority
                    if (hasAuthorization(roles, uroles) == true)
                    {
                        return new Pair(new JipletPrincipal(header
                                .getUsername()), uroles);
                    }
                    else
                    {
                        // send a FORBIDDEN response
                        sendResponse(jiplet, event, Response.FORBIDDEN,
                                "You are not authorized to use this service",
                                null);

                        return null;
                    }
                }
            }
        } // end while

        if (found == false)
        {
            if (JipletLogger.isDebugEnabled() == true)
            {
                JipletLogger
                        .debug("Received a SIP request with no authentication header");
            }

            if (authOnLogout == false)
            {
                if (method.equals(Request.REGISTER) == true)
                {
                    // get the expires header
                    ExpiresHeader expires = (ExpiresHeader) req
                            .getHeader(ExpiresHeader.NAME);
                    if ((expires != null) && (expires.getExpires() == 0))
                    {
                        FromHeader from = (FromHeader) req
                                .getHeader(FromHeader.NAME);
                        String name = ((SipURI) from.getAddress().getURI())
                                .getUser();
                        return new Pair(new JipletPrincipal(name), null);
                    }
                }
            }
        }

        // if we have reached here, it means that either the user has not has
        // prior authentication or the credentials do not match, send a
        // challenge.
        String htype = method.equals(Request.REGISTER) == true ? WWWAuthenticateHeader.NAME
                : ProxyAuthenticateHeader.NAME;

        WWWAuthenticateHeader auth_header = null;

        try
        {
            Thread.currentThread().setContextClassLoader(
                    realm.getClass().getClassLoader());
            auth_header = realm.getAuthenticationHeader(jiplet, req, htype,
                    true);
        }
        finally
        {
            Thread.currentThread().setContextClassLoader(cl);
        }

        if (auth_header == null)
        {
            // the realm is indicating that it does not want to challenge this
            // user.
            // Send a FORBIDDEN response
            sendResponse(jiplet, event, Response.FORBIDDEN,
                    "You are not authorized to use this service", null);

            return null;
        }

        // send a challenge
        int status = method.equals(Request.REGISTER) == true ? Response.UNAUTHORIZED
                : Response.PROXY_AUTHENTICATION_REQUIRED;
        sendResponse(jiplet, event, status, "Please provide your credentials",
                auth_header);
        return null;
    }

    private boolean hasAuthorization(String[] required, String[] has)
    {
        for (int i = 0; i < has.length; i++)
        {
            for (int j = 0; j < required.length; j++)
            {
                if (has[i].equals(required[j]) == true)
                {
                    return true;
                }
            }
        }
        return false;
    }

    private void sendResponse(Jiplet jiplet, RequestEvent event, int response,
            String reason, WWWAuthenticateHeader authHeader)
    {
        ServerTransaction transaction = event.getServerTransaction();

        try
        {
            Response resp = jiplet.getMessageFactory().createResponse(response,
                    event.getRequest());

            if (reason != null)
            {
                resp.setReasonPhrase(reason);
            }

            if (authHeader != null)
            {
                resp.addHeader(authHeader);
            }

            if (transaction != null)
            {
                transaction.sendResponse(resp);
            }
            else
            {
                SipProvider provider = (SipProvider) event.getSource();
                provider.sendResponse(resp);
            }
        }
        catch (Exception e)
        {
            JipletLogger
                    .error("SecurityConstraint for context: "
                            + context.getContext()
                            + " could not send response message in response to the SIP request message: "
                            + " jiplet: " + jiplet.getName() + ", status: "
                            + response + " exception: "
                            + e.getClass().getName() + ": " + e.getMessage());
        }
    }

    /*
     * @see java.util.TimerTask#run()
     */
    public void run()
    {
        long expire = (new Date()).getTime() - cachePeriod;
        authorizations.removeEntries(new Date(expire));
    }

    /**
     * @return Returns the cachePeriod.
     */
    protected long getCachePeriod()
    {
        return cachePeriod;
    }

    /**
     * @param cachePeriod
     *            The cachePeriod to set.
     */
    public void setCachePeriod(long cachePeriod)
    {
        this.cachePeriod = cachePeriod;
    }

    public Pair findSecurityConstraint(Jiplet jiplet)
    {
        synchronized (jipletMap)
        {
            return (Pair) jipletMap.get(jiplet);
        }
    }

    protected boolean isAuthOnLogout()
    {
        return authOnLogout;
    }

    public void setAuthOnLogout(boolean authOnLogout)
    {
        this.authOnLogout = authOnLogout;
    }

    public void removeRealm(Realm realm)
    {
        synchronized (jipletMap)
        {
            Iterator i = jipletMap.entrySet().iterator();
            while (i.hasNext() == true)
            {
                Map.Entry entry = (Map.Entry) i.next();
                Jiplet jiplet = (Jiplet) entry.getKey();
                Pair p = (Pair) entry.getValue();
                Realm r = (Realm) p.getFirst();

                String rname = null;
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                try
                {
                    Thread.currentThread().setContextClassLoader(
                            r.getClass().getClassLoader());
                    rname = realm.getRealmName();
                }
                finally
                {
                    Thread.currentThread().setContextClassLoader(cl);
                }

                if (r.getRealmName().equals(rname) == true)
                {
                    JipletLogger
                            .warn("Removing realm "
                                    + realm.getRealmName()
                                    + " from jiplet "
                                    + jiplet.getName()
                                    + ". No auth. check will be performed for this jiplet anymore.");
                    i.remove();
                }
            }
        }
    }
}
TOP

Related Classes of org.cafesip.jiplet.cma.SecurityConstraintManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.