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.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}