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