/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ejb.easybeans;

import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.security.auth.Subject;

import org.ow2.easybeans.security.api.EZBSecurityContext;
import org.ow2.easybeans.security.struct.JPrincipal;
import org.ow2.jonas.lib.security.context.SecurityContext;

/**
 * Wrapper class for the JOnAS security.<br>
 * It will propagate and read JOnAS security context.
 * @author Florent Benoit
 */
public class JOnASSecurityContextWrapper implements EZBSecurityContext {

    /**
     * Wrapped security context of JOnAS.
     */
    private SecurityContext jonasSecurityContext = null;

    /**
     * Builds a security context around JOnAS security context.
     * @param jonasSecurityContext the JOnAS context
     */
    public JOnASSecurityContextWrapper(final SecurityContext jonasSecurityContext) {
        // Needs to build an empty security context if there is no security context on the current thread.
        if (jonasSecurityContext == null) {
            this.jonasSecurityContext = new SecurityContext();
            org.ow2.jonas.lib.security.context.SecurityCurrent.getCurrent().setSecurityContext(this.jonasSecurityContext);
        } else {
            this.jonasSecurityContext = jonasSecurityContext;
        }
    }

    /**
     * Gets the caller's principal.
     * @param runAsBean if true, the bean is a run-as bean.
     * @return principal of the caller.
     */
    public Principal getCallerPrincipal(final boolean runAsBean) {
        return jonasSecurityContext.getCallerPrincipal(runAsBean);
    }

    /**
     * Gets the caller's roles.
     * @param runAsBean if true, the bean is a run-as bean.
     * @return array of roles of the caller.
     */
    public Principal[] getCallerRoles(final boolean runAsBean) {
        String[] roles = jonasSecurityContext.getCallerPrincipalRoles(runAsBean);

        if (roles == null) {
            throw new IllegalStateException("No roles found on the JOnAS security context");
        }

        Principal[] principals = new Principal[roles.length];
        int i = 0;
        for (String role : roles) {
            principals[i++] = new JPrincipal(role);
        }
        return principals;
    }

    /**
     * Enters in run-as mode with the given subject.<br>
     * The previous subject is stored and will be restored when run-as mode will
     * be ended.
     * @param runAsSubject the subject to used in run-as mode.
     * @return the previous subject.
     */
    public Subject enterRunAs(final Subject runAsSubject) {

        // Get principal name from subject
        String principalName = null;
        for (Principal principal : runAsSubject.getPrincipals(Principal.class)) {
            if (!(principal instanceof Group)) {
                principalName = principal.getName();
                break;
            }
        }

        // Get roles from subject
        List<String> roleList = new ArrayList<String>();
        for (Principal principal : runAsSubject.getPrincipals(Principal.class)) {
            if (principal instanceof Group) {
                Enumeration<? extends Principal> members = ((Group) principal).members();
                while (members.hasMoreElements()) {
                     String role = members.nextElement().getName();
                     roleList.add(role);
                }
            }
        }

        String[] roles = new String[roleList.size()];
        roles = roleList.toArray(roles);

        // Push the RunAs Role
        jonasSecurityContext.pushRunAs(roles[0], principalName, roles);

        // Not used with JOnAS security context
        return null;

    }

    /**
     * Ends the run-as mode and then restore the context stored by container.
     * @param oldSubject subject kept by container and restored.
     */
    public void endsRunAs(final Subject oldSubject) {
        jonasSecurityContext.popRunAs();
    }

}
