    001/*
    002    Licensed to the Apache Software Foundation (ASF) under one
    003    or more contributor license agreements.  See the NOTICE file
    004    distributed with this work for additional information
    005    regarding copyright ownership.  The ASF licenses this file
    006    to you under the Apache License, Version 2.0 (the
    007    "License"); you may not use this file except in compliance
    008    with the License.  You may obtain a copy of the License at
    009
    010       http://www.apache.org/licenses/LICENSE-2.0
    011
    012    Unless required by applicable law or agreed to in writing,
    013    software distributed under the License is distributed on an
    014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    015    KIND, either express or implied.  See the License for the
    016    specific language governing permissions and limitations
    017    under the License.
    018 */
    019package org.apache.wiki.tags;
    020
    021import org.apache.wiki.api.core.Context;
    022import org.apache.wiki.api.core.Engine;
    023import org.apache.wiki.api.core.Session;
    024import org.apache.wiki.api.spi.Wiki;
    025import org.apache.wiki.auth.AuthenticationManager;
    026import org.apache.wiki.auth.GroupPrincipal;
    027import org.apache.wiki.auth.UserManager;
    028import org.apache.wiki.auth.authorize.Role;
    029import org.apache.wiki.auth.user.UserProfile;
    030import org.apache.wiki.i18n.InternationalizationManager;
    031import org.apache.wiki.preferences.Preferences;
    032import org.apache.wiki.util.TextUtil;
    033
    034import jakarta.servlet.http.HttpServletRequest;
    035import java.io.IOException;
    036import java.security.Principal;
    037import java.util.Arrays;
    038import java.util.List;
    039import java.util.ResourceBundle;
    040import java.util.stream.Collectors;
    041
    042/**
    043 * <p>
    044 * Returns user profile attributes, or empty strings if the user has not been
    045 * validated. This tag has a single attribute, "property."
    046 * The <code>property</code> attribute may contain one of the following
    047 * case-insensitive values:
    048 * </p>
    049 * <ul>
    050 * <li><code>created</code> - creation date</li>
    051 * <li><code>email</code> - user's e-mail address</li>
    052 * <li><code>fullname</code> - user's full name</li>
    053 * <li><code>groups</code> - a sorted list of the groups a user belongs to</li>
    054 * <li><code>loginname</code> - user's login name. If the current user does not have a profile, the user's login principal (such as one
    055 * provided by a container login module, user cookie, or anonyous IP address), will supply the login name property</li>
    056 * <li><code>roles</code> - a sorted list of the roles a user possesses</li>
    057 * <li><code>wikiname</code> - user's wiki name</li>
    058 * <li><code>modified</code> - last modification date</li>
    059 * <li><code>exists</code> - evaluates the body of the tag if user's profile exists in the user database</li>
    060 * <li><code>new</code> - evaluates the body of the tag if user's profile does not exist in the user database</li>
    061 * <li><code>canChangeLoginName</code> - always true if custom auth used; also true for container auth and current
    062 * UserDatabase.isSharedWithContainer() is true.</li>
    063 * <li><code>canChangePassword</code> - always true if custom auth used; also true for container auth
    064 * and current UserDatabase.isSharedWithContainer() is true.</li>
    065 * </ul>
    066 * <p>In addition, the values <code>exists</code>, <code>new</code>, <code>canChangeLoginName</code>
    067 * and <code>canChangeLoginName</code> can also be prefixed with <code>!</code> to indicate the
    068 * negative condition (for example, <code>!exists</code>).</p>
    069 *
    070 * @since 2.3
    071 */
    072public class UserProfileTag extends WikiTagBase {
    073
    074    private static final long serialVersionUID = 3258410625431582003L;
    075
    076    public  static final String BLANK = "(not set)";
    077
    078    private static final String CREATED   = "created";
    079    private static final String EMAIL     = "email";
    080    private static final String EXISTS    = "exists";
    081    private static final String NOT_EXISTS= "!exists";
    082    private static final String FULLNAME  = "fullname";
    083    private static final String GROUPS    = "groups";
    084    private static final String LOGINNAME = "loginname";
    085    private static final String MODIFIED  = "modified";
    086    private static final String NEW       = "new";
    087    private static final String NOT_NEW   = "!new";
    088    private static final String ROLES     = "roles";
    089    private static final String WIKINAME  = "wikiname";
    090    private static final String CHANGE_LOGIN_NAME     = "canchangeloginname";
    091    private static final String NOT_CHANGE_LOGIN_NAME = "!canchangeloginname";
    092    private static final String CHANGE_PASSWORD       = "canchangepassword";
    093    private static final String NOT_CHANGE_PASSWORD   = "!canchangepassword";
    094
    095    private String             m_prop;
    096
    097    @Override
    098    public void initTag() {
    099        super.initTag();
    100        m_prop = null;
    101    }
    102
    103    @Override
    104    public final int doWikiStartTag() throws IOException {
    105        final UserManager manager = m_wikiContext.getEngine().getManager( UserManager.class );
    106        final UserProfile profile = manager.getUserProfile( m_wikiContext.getWikiSession() );
    107        String result = null;
    108
    109        if( EXISTS.equals( m_prop ) || NOT_NEW.equals( m_prop ) ) {
    110            return profile.isNew() ? SKIP_BODY : EVAL_BODY_INCLUDE;
    111        } else if( NEW.equals( m_prop ) || NOT_EXISTS.equals( m_prop ) ) {
    112            return profile.isNew() ? EVAL_BODY_INCLUDE : SKIP_BODY;
    113        } else if( CREATED.equals( m_prop ) && profile.getCreated() != null ) {
    114            result = profile.getCreated().toString();
    115        } else if( EMAIL.equals( m_prop ) ) {
    116            result = profile.getEmail();
    117        } else if( FULLNAME.equals( m_prop ) ) {
    118            result = profile.getFullname();
    119        } else if( GROUPS.equals( m_prop ) ) {
    120            result = printGroups( m_wikiContext );
    121        } else if( LOGINNAME.equals( m_prop ) ) {
    122            result = profile.getLoginName();
    123        } else if( MODIFIED.equals( m_prop ) && profile.getLastModified() != null ) {
    124            result = profile.getLastModified().toString();
    125        } else if( ROLES.equals( m_prop ) ) {
    126            result = printRoles( m_wikiContext );
    127        } else if( WIKINAME.equals( m_prop ) ) {
    128            result = profile.getWikiName();
    129
    130            if( result == null ) {
    131                //
    132                //  Default back to the declared user name
    133                //
    134                final Engine engine = this.m_wikiContext.getEngine();
    135                final Session wikiSession = Wiki.session().find( engine, ( HttpServletRequest )pageContext.getRequest() );
    136                final Principal user = wikiSession.getUserPrincipal();
    137
    138                if( user != null ) {
    139                    result = user.getName();
    140                }
    141            }
    142        } else if( CHANGE_PASSWORD.equals( m_prop ) || CHANGE_LOGIN_NAME.equals( m_prop ) ) {
    143            final AuthenticationManager authMgr = m_wikiContext.getEngine().getManager( AuthenticationManager.class );
    144            if( !authMgr.isContainerAuthenticated() ) {
    145                return EVAL_BODY_INCLUDE;
    146            }
    147        } else if( NOT_CHANGE_PASSWORD.equals( m_prop ) || NOT_CHANGE_LOGIN_NAME.equals( m_prop ) ) {
    148            final AuthenticationManager authMgr = m_wikiContext.getEngine().getManager( AuthenticationManager.class );
    149            if( authMgr.isContainerAuthenticated() ) {
    150                return EVAL_BODY_INCLUDE;
    151            }
    152        }
    153
    154        if( result != null ) {
    155            pageContext.getOut().print( TextUtil.replaceEntities( result ) );
    156        }
    157        return SKIP_BODY;
    158    }
    159
    160    public void setProperty( final String property )
    161    {
    162        m_prop = property.toLowerCase().trim();
    163    }
    164
    165    /**
    166     * Returns a sorted list of the {@link org.apache.wiki.auth.authorize.Group} objects a user possesses
    167     * in his or her Session. The result is computed by consulting
    168     * {@link org.apache.wiki.api.core.Session#getRoles()}
    169     * and extracting those that are of type Group.
    170     * @return the list of groups, sorted by name
    171     */
    172    public static String printGroups( final Context context ) {
    173        final Principal[] roles = context.getWikiSession().getRoles();
    174        final List< String > tempRoles;
    175        final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE );
    176
    177        tempRoles = Arrays.stream(roles).filter(role -> role instanceof GroupPrincipal).map(Principal::getName).collect(Collectors.toList());
    178        if(tempRoles.isEmpty()) {
    179            return rb.getString( "userprofile.nogroups" );
    180        }
    181
    182        final StringBuilder sb = new StringBuilder();
    183        for( int i = 0; i < tempRoles.size(); i++ ) {
    184            final String name = tempRoles.get( i );
    185
    186            sb.append( name );
    187            if( i < ( tempRoles.size() - 1 ) ) {
    188                sb.append( ',' );
    189                sb.append( ' ' );
    190            }
    191
    192        }
    193        return sb.toString();
    194    }
    195
    196    /**
    197     * Returns a sorted list of the {@link org.apache.wiki.auth.authorize.Role} objects a user possesses
    198     * in his or her Session. The result is computed by consulting
    199     * {@link org.apache.wiki.api.core.Session#getRoles()}
    200     * and extracting those that are of type Role.
    201     * @return the list of roles, sorted by name
    202     */
    203    public static String printRoles( final Context context ) {
    204        final Principal[] roles = context.getWikiSession().getRoles();
    205        final List< String > tempRoles;
    206        final ResourceBundle rb = Preferences.getBundle( context, InternationalizationManager.CORE_BUNDLE );
    207
    208        tempRoles = Arrays.stream(roles).filter(role -> role instanceof Role).map(Principal::getName).collect(Collectors.toList());
    209        if(tempRoles.isEmpty()) {
    210            return rb.getString( "userprofile.noroles" );
    211        }
    212
    213        final StringBuilder sb = new StringBuilder();
    214        for( int i = 0; i < tempRoles.size(); i++ ) {
    215            final String name = tempRoles.get( i );
    216
    217            sb.append( name );
    218            if( i < ( tempRoles.size() - 1 ) ) {
    219                sb.append( ',' );
    220                sb.append( ' ' );
    221            }
    222
    223        }
    224        return sb.toString();
    225    }
    226
    227}