com.xwiki.authentication.sts.XWikiSTSAuthenticator.java Source code

Java tutorial

Introduction

Here is the source code for com.xwiki.authentication.sts.XWikiSTSAuthenticator.java

Source

/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This 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 software 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package com.xwiki.authentication.sts;

import com.xpn.xwiki.*;
import com.xpn.xwiki.doc.*;
import com.xpn.xwiki.objects.*;
import com.xpn.xwiki.user.api.XWikiUser;
import com.xpn.xwiki.user.impl.xwiki.XWikiAuthServiceImpl;
import com.xpn.xwiki.web.XWikiRequest;
import java.io.*;
import java.net.URI;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opensaml.*;
import org.opensaml.xml.*;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;

/**
 * Authentication based on Trust Security Token Service. Some parameters can be
 * used to customized its behavior in xwiki.cfg.
 * Based on context - is showingLogin, can return XWiki User object according to
 * context parametr. Implements XWiki - authentication and have methods to show login
 * and everything this is making according the context. 
 * 
 * @version $Id$
 */
public class XWikiSTSAuthenticator extends XWikiAuthServiceImpl {
    /** 
     * props - Props Variable - Holding method to load Certificate from file
     */
    private static Log log = LogFactory.getLog(XWikiSTSAuthenticator.class);
    /**
    * Holds the mapping between HTTP header fields names and XWiki user
    */
    private static Map<String, String> userMappings;
    /** 
     * props - Props Variable - Holding method to load Certificate from file
     */
    private static Props props = new Props();
    /**
    *  Error collector - collecting errors in a List. Converting to strings
    */
    private STSErrorCollector errorCollector = new STSErrorCollector();

    /**
     * showLogin - Makes appropriate url and sends request to the STS (Security Token Service)  
     * and gets response with xwiki methods.
     * 
     * @param context XWikiContext - context - to make request and show login
     * @throws XWikiException java.lang.Object extended by java.lang.Throwable </br> extended by java.lang.Exception extended by com.xpn.xwiki.XWikiException
     */
    @Override
    public void showLogin(XWikiContext context) throws XWikiException {
        log.trace("showLogin()");
        XWikiRequest request = context.getRequest();
        try {
            DefaultBootstrap.bootstrap();
        } catch (ConfigurationException e) {
            log.error("Failed to bootstrap sts module" + e);
            errorCollector.addError(new Throwable("Failed to bootstrap sts module: ", e));
        }

        // STS provider URL
        String url = props.getAuthURL(context) + "?wa=wsignin1.0";
        // Request realm
        String wtrealm = props.getWtrealm(context);
        if (wtrealm != null && wtrealm != "")
            url += "&wtrealm=" + escapeHtml(wtrealm);
        // Request ID
        String wctx = props.getWctx(context);
        if (wctx != null && "1".equals(wctx)) {
            String randId = RandomStringUtils.randomAlphanumeric(42);
            log.debug("Request ID: " + randId);
            request.getSession().setAttribute("saml_id", randId);
            url += "&wctx=" + randId;
        }
        // Host is set manually, because XWiki is behind proxy server
        // and simple XWiki.getRequestURL(request) returns localhost
        String wreplyHost = props.getWreplyHost(context);
        String wreplyPage = props.getWreplyPage(context);
        String page = "/";
        if (wreplyHost != null && !"0".equals(wreplyHost)) {
            if ("1".equals(wreplyPage) || "shorten".equals(wreplyPage)) {
                page = request.getParameter("xredirect");
                if (page != null)
                    log.trace("Got xrecdirect to: " + page);
                else
                    page = XWiki.getRequestURL(request).getFile();
                if ("shorten".equals(wreplyPage)) {
                    // change reply address if URL shortening is used
                    page = page.replace("/xwiki/bin/view/", "/");
                    page = page.replace("/WebHome", "/");
                }
                log.trace("Reply page: " + page);
            }
            url += "&wreply=" + escapeHtml(wreplyHost + page);
        }
        request.getSession().setAttribute("saml_url", wreplyHost + page);

        // Auth request time
        String wct = props.getWct(context);
        if (wct != null && "1".equals(wct)) {
            SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
            dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
            wct = dateFormatGmt.format(new Date());
            url += "&wct=" + wct;
        }
        // maximum age of authentication
        String wfresh = props.getWfresh(context);
        if (wfresh != null && wfresh != "" && Integer.parseInt(wfresh) > 0) {
            url += "&wfresh=" + wfresh;
        }
        // Send request to the STS service
        log.info("SAML STS request sent to " + url);

        try {
            context.getResponse().sendRedirect(url);
            context.setFinished(true);
        } catch (Exception e) {
            log.debug("Cannot call sendRedirect() after the response has been committed: " + e);
        }

    }

    /**
     * checkSTSResponse - Check Response of (Security Token Service)  
     * This method is trying to create document using XWikiContext context argument and checking is it done or not? 
     * @param context XWikiContext - XWikiContext to check is it have right data
     * @return boolen - true - if check - ok, false - fault to create doc from test data etc.
     * @throws XWikiException java.lang.Object extended by java.lang.Throwable </br> extended by java.lang.Exception extended by com.xpn.xwiki.XWikiException
    * @throws ConfigurationException
    */
    public boolean checkSTSResponse(XWikiContext context) throws XWikiException {
        // read from STSResponse
        log.trace("checkSTSResponse()");
        XWikiRequest request = context.getRequest();
        Map<String, String> attributes = new HashMap<String, String>();
        String authType = "";
        try {
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                log.trace(headerName + ": " + request.getHeader(headerName));
            }
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                log.trace(paramName + ": " + request.getParameter(paramName));
            }
        } catch (Exception e) {
            log.error("Failed to read request headers or parameters: " + e);
            errorCollector.addError(new Throwable("Failed to read request headers or parameters: ", e));
        }

        String stsResponse = request.getParameter("wresult");
        if (stsResponse == null) {
            log.debug("Didn't get wresult from request!");
            log.error(errorCollector.listErrors());
            errorCollector.clearErrorList();

            return false;
        }

        log.debug("\n***** STS Response: *****\n" + stsResponse + "\n*****");
        HttpServletRequest myRequest = context.getRequest().getHttpServletRequest();
        try {
            log.debug("request.getParameter('wresult') is " + myRequest.getParameter("wresult"));
            log.debug("request.getParameter('wct') is " + myRequest.getParameter("wct"));
            log.debug("request.getParameter('wctx') is " + myRequest.getParameter("wctx"));
            log.debug("request.getParameter('wa') is " + myRequest.getParameter("wa"));
        } catch (Exception e) {
            log.error("Failed to read response request parameters" + e);
            errorCollector.addError(new Throwable("Failed to read response request parameters: ", e));
        }

        // Get Context ID from the user session
        String prevId = (String) request.getSession().getAttribute("saml_id");
        String curId = myRequest.getParameter("wctx");
        String wctx = props.getWctx(context);
        // Check response and token
        if (wctx != null && "1".equals(wctx)) {
            try { // Check token validity
                if (!prevId.equals(curId)) {
                    log.debug("Retrieved wctx parameter value doesn't match passed value. Passed: " + prevId
                            + " retrieved: " + curId);
                    return false;
                }
                STSTokenValidator validator = new STSTokenValidator();
                validator.setSTSErrorCollector(errorCollector);
                validator.setContext(prevId);
                // Get parameters from Xwiki configuration
                validator.setIssuer(props.getIssuer(context));
                log.debug("props.getIssuer(context) " + props.getIssuer(context));
                log.debug("checkSTSResponse(props.getIssuer(context)) " + props.getIssuer(context));
                STSTokenValidator.setEntityId(props.getEntityId(context));
                validator.setIssuerDN(props.getIssuerDN(context));
                List<String> subjectDNs = new ArrayList<String>();
                subjectDNs.add(props.getSubjectDNs(context));
                validator.setSubjectDNs(subjectDNs);
                List<URI> audienceURIs = new ArrayList<URI>();
                audienceURIs.add(new URI(props.getAudienceURIs(context)));
                validator.setAudienceUris(audienceURIs);
                String wct = props.getWct(context);
                validator.setCertificate(props.getCertificate(context));
                // If time control is set, use time validation
                if (wct != null && "1".equals(wct)) {
                    int maxClockSkew = Integer.parseInt(props.getWfresh(context)) * 60 * 1000;
                    validator.setMaxClockSkew(maxClockSkew);
                    validator.setValidateExpiration(true);
                } else
                    validator.setValidateExpiration(false);
                List<STSClaim> claims = validator.validate(myRequest.getParameter("wresult"));
                log.trace("Token claims: " + claims);

            } catch (Exception e) {
                // as validator returns validation errors as exceptions
                // log them only in debug mode
                log.error("Failed to validate token\n" + e);
                errorCollector.addError(new Throwable("Failed to validate token: ", e));
                return false;
            }

            try {
                DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                DocumentBuilder db = dbf.newDocumentBuilder();
                Document doc = db.parse(new ByteArrayInputStream(stsResponse.getBytes("utf-8")));
                doc.getDocumentElement().normalize();
                log.debug("Root element:" + doc.getDocumentElement().getNodeName());
                NodeList nodeList = doc.getElementsByTagName("saml:Attribute");
                log.debug("Node list length:" + nodeList.getLength());
                // Process all attributes
                for (int i = 0; i < nodeList.getLength(); i++) {
                    Node node = nodeList.item(i);
                    String attrName = node.getAttributes().getNamedItem("AttributeName").getTextContent();
                    String attrValue = node.getChildNodes().item(0).getTextContent();
                    // CAPITAL
                    if ("CAPITAL".equals(props.getDataFormat(context)))
                        attrValue = attrValue.toUpperCase();
                    // Title
                    if ("Title".equals(props.getDataFormat(context)))
                        attrValue = attrValue.substring(0, 1).toUpperCase()
                                + ((attrValue.length() > 1) ? attrValue.substring(1).toLowerCase() : "");
                    log.debug("Node:" + attrName + ", value:" + attrValue);
                    attributes.put(attrName, attrValue);
                }
                // get authentication method (should be known for legal reasons)
                authType = doc.getElementsByTagName("saml:AuthenticationStatement").item(0).getAttributes()
                        .getNamedItem("AuthenticationMethod").getNodeValue();
            } catch (Exception e) {
                log.error("Failed reading STS response\n" + e);
                errorCollector.addError(new Throwable("Failed reading STS response: ", e));
                return false;
            }
        } else {
            log.warn("Response and token were not validated! To validate, set xwiki.authentication.sts.wctx=1");
        }

        // let's map the data
        Map<String, String> userData = getExtendedInformation(attributes, context);
        String personID = attributes.get(props.getIdField(context));
        // set conventional person code format for NORDEA, PAREX, SEB banks
        String person_ID;
        if (personID != null && personID.indexOf('-') < 0 && personID.length() == 11) {
            person_ID = personID.substring(0, 6) + "-" + personID.substring(6);
            log.debug("Changed person ID from " + personID + " to " + person_ID);
        } else
            person_ID = personID;
        log.debug("STS person ID is " + person_ID);
        log.debug("STS attributes are " + attributes);
        log.debug("STS user data are " + userData);
        // Get STSAuthClass ID field from configuration file (for backward
        // compatibility)
        // TODO should change nameid to personid for all XWiki solutions
        String stsAuthClassIdField = props.getStsAuthClassIdField(context);
        String sql = "select distinct doc.fullName from XWikiDocument as doc, BaseObject as obj, StringProperty as nameidprop where"
                + " doc.fullName=obj.name and obj.className='XWiki.STSAuthClass' and obj.id=nameidprop.id.id and nameidprop.id.name='"
                + stsAuthClassIdField + "' and nameidprop.value='" + person_ID + "'";
        log.debug("XWiki search SQL string: " + sql);
        List<Object> list = context.getWiki().search(sql, context);
        String validFullUserName = null;
        String validUserName = null;

        if (list.isEmpty()) {
            // User does not exist. Let's generate a unique page name
            log.debug("Did not find XWiki User. Generating it.");
            String userName = generateXWikiUsername(userData, context);
            if ("".equals(userName))
                userName = "User";
            validUserName = context.getWiki().getUniquePageName("XWiki", userName, context);
            validFullUserName = "XWiki." + validUserName;
            log.debug("Generated XWiki User Name " + validFullUserName);

        } else {
            validFullUserName = (String) list.get(0);
            log.debug("Found XWiki User " + validFullUserName);

        }

        // we found a user or generated a unique user name
        if (validFullUserName != null) {
            // check if we need to create/update a user page
            String database = context.getDatabase();
            try {
                // Switch to main wiki to force users to be global users
                context.setDatabase(context.getMainXWiki());

                // test if user already exists
                if (!context.getWiki().exists(validFullUserName, context)) {
                    log.debug("Need to create user " + validFullUserName);

                    // create user
                    userData.put("active", "1");

                    int result = context.getWiki().createUser(validUserName, userData, "XWiki.XWikiUsers",
                            "#includeForm(\"XWiki.XWikiUserSheet\")", "edit", context);
                    if (result < 0) {
                        log.error("Failed to create user " + validFullUserName + " with code " + result);
                        errorCollector.addError(new Throwable("Failed to create user: "));
                        return false;
                    }
                    XWikiDocument userDoc = context.getWiki().getDocument(validFullUserName, context);
                    BaseObject userObj = userDoc.getObject("XWiki.XWikiUsers");
                    // Fix bug for e-mail field where "$email" value is set on
                    // creation
                    userObj.set("email", "", context);
                    // set user profile to read only with explicit view to not
                    // allow
                    // changing his
                    // name, surname and password
                    BaseObject rightsObj = userDoc.getObject("XWiki.XWikiRights", 1);
                    rightsObj.set("allow", 1, context);
                    rightsObj.set("levels", "view", context);
                    // set person ID and authentication type
                    BaseObject stsObj = userDoc.newObject("XWiki.STSAuthClass", context);
                    stsObj.set("nameid", person_ID, context);
                    stsObj.set("authtype", authType, context);
                    context.getWiki().saveDocument(userDoc, context);
                    log.info("New user " + validFullUserName + " has been successfully created. Nameid: "
                            + person_ID + " authtype: " + authType);

                } else {

                    XWikiDocument userDoc = context.getWiki().getDocument(validFullUserName, context);
                    BaseObject userObj = userDoc.getObject("XWiki.XWikiUsers");
                    boolean updated = false;
                    for (Map.Entry<String, String> entry : userData.entrySet()) {
                        String field = entry.getKey();
                        String value = entry.getValue();
                        BaseProperty prop = (BaseProperty) userObj.get(field);
                        String currentValue = (prop == null || prop.getValue() == null) ? null
                                : prop.getValue().toString();
                        if (value != null && !value.equals(currentValue)) {
                            userObj.set(field, value, context);
                            updated = true;
                        }

                    }
                    BaseObject stsObj = userDoc.getObject("XWiki.STSAuthClass");
                    BaseProperty prop = (BaseProperty) stsObj.get("authtype");
                    String currenAuthType = prop.getValue().toString();
                    log.debug("currenAuthType: " + currenAuthType);
                    if (!authType.equals(currenAuthType)) {
                        stsObj.set("authtype", authType, context);
                        updated = true;
                    }
                    if (updated) {
                        context.getWiki().saveDocument(userDoc, context);
                        log.info("Existing user " + validFullUserName + " has been successfully updated. Nameid: "
                                + person_ID + " authtype: " + authType);
                    } else
                        log.info("Existing user " + validFullUserName
                                + " was found. Properties were not changed. Nameid: " + person_ID + " authtype: "
                                + authType);
                }
            } catch (Exception e) {
                log.error("Failed to create user " + validFullUserName + "\n" + e);
                errorCollector.addError(new Throwable("Failed to create user: ", e));
                return false;
            } finally {
                context.setDatabase(database);
            }

        }

        log.debug("Setting authentication in session for user " + validFullUserName);
        // mark that we have authenticated the user in the session
        context.getRequest().getSession().setAttribute(props.getAuthField(context), validFullUserName);

        // need to redirect now
        String sourceurl = (String) request.getSession().getAttribute("saml_url");

        log.debug("Redirecting after valid authentication to " + sourceurl);
        try {
            context.getResponse().sendRedirect(sourceurl);
            context.setFinished(true);
            return true;
        } catch (Exception e) {
            log.error("Failed to redirect after authentication\n" + e);
            errorCollector.addError(new Throwable("Failed to redirect after authentication: ", e));
        }
        return false;
    }

    /**
     * checkAuth - Checks authentification session in cookies. If there is data about current user
     * returns it. If there is not an authentification data - then method is trying to login
     * using methadata creating new XWiki Object.
     * 
     * @param context XWikiContext - context of XWiki Engine
     * @throws XWikiUser java.lang.Object extended by java.lang.Throwable </br> extended by java.lang.Exception extended by com.xpn.xwiki.XWikiException
     *
    * @see com.xpn.xwiki.user.impl.xwiki.AppServerTrustedAuthServiceImpl#checkAuth(com.xpn.xwiki.XWikiContext)
    */
    @Override
    public XWikiUser checkAuth(XWikiContext context) throws XWikiException {
        log.trace("checkAuth(context)");
        try {
            XWikiRequest request = context.getRequest();
            log.trace("context================\n" + context);
            log.trace("request headers============");
            Enumeration<String> headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String headerName = headerNames.nextElement();
                log.trace(headerName + "=" + request.getHeader(headerName));
            }
            Enumeration<String> en = request.getParameterNames();
            log.trace("request parameters=============");
            while (en.hasMoreElements()) {
                String paramName = en.nextElement();
                String paramValue = request.getParameter(paramName);
                log.trace(paramName + "=" + URLEncoder.encode(paramValue));
            }
        } catch (Exception e) {
            log.error("Got error during printing request parameters: " + e);
            errorCollector.addError(new Throwable("Got error during printing request parameters: ", e));
        }

        // check in the session if the user is already authenticated
        String stsUserName = (String) context.getRequest().getSession().getAttribute(props.getAuthField(context));
        if (stsUserName == null) {
            // check standard authentication
            if (context.getRequest().getCookie("username") != null || "logout".equals(context.getAction())
                    || context.getAction().startsWith("login")
                    || "1".equals(context.getRequest().getParameter("basicauth"))) {
                log.debug("Fallback to standard authentication");
                return super.checkAuth(context);
            }
            // check if we have a STS Response to verify
            // (this sets getAuthField value for the next pass)
            if (checkSTSResponse(context))
                return null;
        } else {
            log.debug("Found authentication of user " + stsUserName);
            log.info(errorCollector.listErrors());
            if (context.isMainWiki()) {
                return new XWikiUser(stsUserName);
            } else {
                return new XWikiUser(context.getMainXWiki() + ":" + stsUserName);
            }
        }
        return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see com.xpn.xwiki.user.impl.xwiki.AppServerTrustedAuthServiceImpl#checkAuth(java.lang.String,
     *      java.lang.String, java.lang.String, com.xpn.xwiki.XWikiContext)
     */
    @Override
    public XWikiUser checkAuth(String username, String password, String rememberme, XWikiContext context)
            throws XWikiException {
        log.trace("checkAuth(): " + username + ", " + password + ", " + rememberme);
        String auth = getAuthFieldValue(context);
        if ((auth == null) || "".equals(auth)) {
            return super.checkAuth(context);
        } else {
            return checkAuth(context);
        }
    }

    /**
     * getter to get authField (value) from XWikiContext
     * 
     * @param context XWikiContext - context of XWiki Engine
     * @throws XWikiUser java.lang.Object extended by java.lang.Throwable </br> extended by java.lang.Exception extended by com.xpn.xwiki.XWikiException
     *
    * @see com.xpn.xwiki.user.impl.xwiki.AppServerTrustedAuthServiceImpl#checkAuth(com.xpn.xwiki.XWikiContext)
    */
    private String getAuthFieldValue(XWikiContext context) {
        String val = (String) context.getRequest().getSession(true).getAttribute(props.getAuthField(context));
        log.trace("getAuthFieldValue(): " + val);
        return val;
    }

    /**
     * getExtendedInformation 
     * Get Extended Information from context according to data parameter
     * 
     * @param data Map - data acccording which - will be extracted extended information
     * @param context XWikiContext - context to get data from
     * @return mapped information in format Map<String, String> 
    * @see com.xpn.xwiki.user.impl.xwiki.AppServerTrustedAuthServiceImpl#checkAuth(com.xpn.xwiki.XWikiContext)
    */
    private Map<String, String> getExtendedInformation(Map data, XWikiContext context) {
        log.trace("ExtendedInformation()");
        Map<String, String> extInfos = new HashMap<String, String>();
        for (Map.Entry<String, String> entry : getFieldMapping(context).entrySet()) {
            String dataValue = (String) data.get(entry.getKey());
            log.trace(" STS:" + entry.getKey() + ", value:" + dataValue + ", xwiki field:" + entry.getValue());
            if (dataValue != null) {
                extInfos.put(entry.getValue(), dataValue);
            }
        }
        return extInfos;
    }

    /**
     * @param context
     *            the XWiki context.
     * @return the fields to use to generate the xwiki user name
     */
    private String[] getXWikiUsernameRule(XWikiContext context) {
        String userFields = props.getUsernameRule(context);
        log.trace("XWikiUsernameRule(): " + userFields);
        return userFields.split(",");
    }

    /**
     * generateXWikiUsername(Map userData, XWikiContext context)
     * generate username according to XWikiContext and userData fields
     * @param userData Map - data acccording which - will be extracted extended information
     * @param context XWikiContext - context to get data from
     * @return userName String
    * @see com.xpn.xwiki.user.impl.xwiki.AppServerTrustedAuthServiceImpl#checkAuth(com.xpn.xwiki.XWikiContext)
    */
    private String generateXWikiUsername(Map userData, XWikiContext context) {
        log.trace("generateXWikiUsername()");
        String[] userFields = getXWikiUsernameRule(context);
        StringBuilder userName = new StringBuilder("");
        for (String field : userFields) {
            String value = (String) userData.get(field);
            if (value != null && value.length() > 0) {
                userName.append(value);
            }
        }
        log.debug("XWikiUsername: " + userName);
        return userName.toString();
    }

    /**
     * @param context
     *            the XWiki context.
     * @return the mapping between HTTP header fields names and XWiki user
     *         profile fields names.
     */
    private static Map<String, String> getFieldMapping(XWikiContext context) {
        log.trace("getFieldMapping()");
        if (userMappings == null) {
            userMappings = new HashMap<String, String>();

            String fieldMapping = props.getFieldMapping(context);
            String[] fields = fieldMapping.split(",");

            for (int j = 0; j < fields.length; j++) {
                String[] field = fields[j].split("=");
                if (2 == field.length) {
                    String xwikiattr = field[0].trim();
                    String headerattr = field[1].trim();
                    userMappings.put(headerattr, xwikiattr);
                } else {
                    log.error("Error parsing STS fields_mapping attribute in xwiki.cfg: " + fields[j]);
                }
            }
        }
        return userMappings;
    }

    /**
     * Put listed errors into log
     */
    public void listErrors() {
        log.info(errorCollector.listErrors());
        errorCollector.clearErrorList();
    }
}