a4544a5a0e622ef69e38641f87ab1b5685e05911Phill Cunnington/*
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * The contents of this file are subject to the terms
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * of the Common Development and Distribution License
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * (the License). You may not use this file except in
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * compliance with the License.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * You can obtain a copy of the License at
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * https://opensso.dev.java.net/public/CDDLv1.0.html or
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * opensso/legal/CDDLv1.0.txt
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * See the License for the specific language governing
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * permission and limitations under the License.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * When distributing Covered Code, include this CDDL
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Header Notice in each file and include the License file
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * at opensso/legal/CDDLv1.0.txt.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * If applicable, add the following below the CDDL Header,
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * with the fields enclosed by brackets [] replaced by
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * your own identifying information:
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * "Portions Copyrighted [year] [name of copyright owner]"
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * $Id: IDPPAuthorizer.java,v 1.6 2008/08/19 19:12:22 veiming Exp $
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *
a4544a5a0e622ef69e38641f87ab1b5685e05911Phill Cunnington * Portions Copyrighted 2015 ForgeRock AS.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster */
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterpackage com.sun.identity.liberty.ws.idpp.plugin;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.iplanet.sso.SSOToken;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.policy.*;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.policy.interfaces.Condition;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.Map;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.HashMap;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.Iterator;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.ResourceBundle;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.Set;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.HashSet;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport java.util.List;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.liberty.ws.interfaces.Authorizer;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.liberty.ws.idpp.common.*;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.shared.debug.Debug;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterimport com.sun.identity.shared.locale.Locale;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster/**
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * The class <code>IDPPAuthorizer</code> is the default implementation of
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * IDPP Authorization and implements <code>Authorizer</code>. It
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * provides an allow ,deny, interact for consent, interact for value action
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * based on the resource that a WSC is requesting for.
8d3140b524c0e28c0a49dc7c7d481123ef3cfe11Chris Lee * This makes use of the existing OpenAM policy framework by defining
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * policy as action values in the personal profile service.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster */
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Fosterpublic class IDPPAuthorizer implements Authorizer {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster private static ResourceBundle bundle =
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Locale.getInstallResourceBundle("fmPersonalProfile");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster private static Debug debug = Debug.getInstance("libIDWSF");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster private static PolicyEvaluator evaluator = null;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster static {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster try {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster evaluator = new PolicyEvaluator(IDPPConstants.IDPP_SERVICE);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster } catch (Exception ex) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.error("IDPPAuthorizer:Static Init failed", ex);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster /**
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster *Default constructor
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster */
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster public IDPPAuthorizer() {}
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster /**
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Checks whether this is authorized
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * IDPPAuthorizer implements this class.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param credential credential
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param action action
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param data object
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param env env map
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @return true if authorized, otherwise false.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster */
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster public boolean isAuthorized(Object credential, String action,
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Object data, Map env) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return false;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster /**
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Returns authorization decision to query or modify the select data
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param credential SSOToken of a WSC.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param action request action.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param data Object who is being accessed.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @param env A Map contains information useful for policy evaluation.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * The following key is defined and its value should be passed in:
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Key: <code>USER_ID</code>
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Value: id of the user whose resource is being accessed.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Key: <code>AUTH_TYPE</code>
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Value: The authentication mechanism WSC used.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Key: <code>MESSAGE</code>
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * Value:
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * <code>com.sun.identity.liberty.ws.soapbinding.Message</code>.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @return Object AuthorizationDecision object contains authorization
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * decision information for the given data.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * For Personal Profile service, this object would be the
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * String authZ decision value.
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster * @exception Exception
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster */
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster public Object getAuthorizationDecision(
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Object credential,
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster String action,
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Object data,
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster java.util.Map env)
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster throws Exception {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorizationDecision:Init");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(credential == null || action == null || data == null) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.error("IDPPAuthorizer.isAuthorized:null input");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster throw new Exception(
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster bundle.getString("nullInputParams"));
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster try {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster SSOToken token = (SSOToken)credential;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster String resource = (String)data;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Set actions = new HashSet(1);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster actions.add(action);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Map map = null;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster String userid = (String) env.get(USER_ID);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if (debug.messageEnabled()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorizationDecision: uid="
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster + userid);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if ((userid != null) && (userid.length() != 0)) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster HashSet set = new HashSet();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster set.add(userid);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster map = new HashMap();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster map.put(Condition.INVOCATOR_PRINCIPAL_UUID, set);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster PolicyDecision policyDecision = evaluator.getPolicyDecision(
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster token, resource, actions, map);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(policyDecision == null) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(debug.messageEnabled()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorization" +
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster "Decision:PolicyDecision is null");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.AUTHZ_DENY;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Map actionDecisions = policyDecision.getActionDecisions();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster ActionDecision actionDecision = (ActionDecision)
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster actionDecisions.get(action);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(actionDecision == null) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(debug.messageEnabled()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorization" +
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster "Decision:ActionDecision is null");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.AUTHZ_DENY;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Set values = (Set)actionDecision.getValues();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(values == null || values.isEmpty()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(debug.messageEnabled()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorization" +
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster "Decision:values are null");
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.AUTHZ_DENY;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(debug.messageEnabled()) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.message("IDPPAuthorizer.getAuthorization" +
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster "Decision: action values:" + values);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(values.contains(IDPPConstants.AUTHZ_DENY)) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.AUTHZ_DENY;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(values.contains(IDPPConstants.INTERACT_FOR_VALUE)) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.INTERACT_FOR_VALUE;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster if(values.contains(IDPPConstants.INTERACT_FOR_CONSENT)) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return IDPPConstants.INTERACT_FOR_CONSENT;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster Iterator iter = values.iterator();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster return (String)iter.next();
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster } catch (Exception ex) {
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster debug.error("IDPPAuthorizer.getAuthorizationDecision:"+
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster "Exception during authorization.", ex);
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster throw ex;
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster }
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster
4a2f0f0be43dfd4c1b490cbf3cc48b6ba6084b1cAllan Foster}