• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CeON / dataverse / 1371

28 Jun 2024 05:54AM UTC coverage: 25.453% (-0.05%) from 25.503%
1371

push

jenkins

web-flow
Closes #2474: Store the entity id in saml session once user logs in with the identity provider (#2486)

49 of 54 new or added lines in 4 files covered. (90.74%)

860 existing lines in 14 files now uncovered.

17805 of 69953 relevant lines covered (25.45%)

0.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/Shib.java
1
package edu.harvard.iq.dataverse;
2

3
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
4
import edu.harvard.iq.dataverse.authorization.UserRecordIdentifier;
5
import edu.harvard.iq.dataverse.authorization.groups.GroupServiceBean;
6
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibAuthenticationProvider;
7
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibServiceBean;
8
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibUserNameFields;
9
import edu.harvard.iq.dataverse.authorization.providers.shib.ShibUtil;
10
import edu.harvard.iq.dataverse.common.BundleUtil;
11
import edu.harvard.iq.dataverse.consent.ConsentDto;
12
import edu.harvard.iq.dataverse.consent.ConsentService;
13
import edu.harvard.iq.dataverse.notification.UserNotificationService;
14
import edu.harvard.iq.dataverse.persistence.config.EMailValidator;
15
import edu.harvard.iq.dataverse.persistence.dataverse.Dataverse;
16
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUser;
17
import edu.harvard.iq.dataverse.persistence.user.AuthenticatedUserDisplayInfo;
18
import edu.harvard.iq.dataverse.persistence.user.BuiltinUser;
19
import edu.harvard.iq.dataverse.persistence.user.NotificationType;
20
import edu.harvard.iq.dataverse.settings.SettingsServiceBean;
21
import edu.harvard.iq.dataverse.settings.SettingsServiceBean.Key;
22
import edu.harvard.iq.dataverse.settings.SettingsWrapper;
23
import edu.harvard.iq.dataverse.util.JsfHelper;
24
import edu.harvard.iq.dataverse.util.SystemConfig;
25
import io.vavr.control.Option;
26
import org.apache.commons.lang.StringUtils;
27
import org.omnifaces.cdi.ViewScoped;
28
import org.slf4j.Logger;
29
import org.slf4j.LoggerFactory;
30

31
import javax.ejb.EJB;
32
import javax.ejb.EJBException;
33
import javax.faces.application.FacesMessage;
34
import javax.faces.component.UIComponent;
35
import javax.faces.component.UIInput;
36
import javax.faces.context.ExternalContext;
37
import javax.faces.context.FacesContext;
38
import javax.inject.Inject;
39
import javax.inject.Named;
40
import javax.servlet.http.HttpServletRequest;
41
import java.io.IOException;
42
import java.nio.charset.StandardCharsets;
43
import java.sql.Timestamp;
44
import java.util.ArrayList;
45
import java.util.Date;
46
import java.util.List;
47
import java.util.Locale;
48
import java.util.Objects;
49

50
@ViewScoped
51
@Named("Shib")
52
public class Shib implements java.io.Serializable {
×
53

54
    private static final Logger logger = LoggerFactory.getLogger(Shib.class);
×
55

56
    @Inject
57
    DataverseSession session;
58

59
    @EJB
60
    AuthenticationServiceBean authSvc;
61
    @EJB
62
    ShibServiceBean shibService;
63
    @EJB
64
    DataverseDao dataverseDao;
65
    @EJB
66
    GroupServiceBean groupService;
67
    @EJB
68
    UserNotificationService userNotificationService;
69
    @Inject
70
    private SettingsWrapper settingsWrapper;
71
    @Inject
72
    private SystemConfig systemConfig;
73
    @Inject
74
    private SettingsServiceBean settingsService;
75

76
    @Inject
77
    private ConsentService consentService;
78

79
    private List<ConsentDto> consents = new ArrayList<>();
×
80

81
    HttpServletRequest request;
82
    private String userPersistentId;
83
    private String internalUserIdentifer;
84
    AuthenticatedUserDisplayInfo displayInfo;
85
    /**
86
     * @todo Remove this boolean some day? Now the mockups show a popup. Should
87
     * be re-worked. See also the comment about the lack of a Cancel button.
88
     */
89
    private boolean visibleTermsOfUse;
90
    private final String loginpage = "/loginpage.xhtml";
×
91
    private final String identityProviderProblem = "Problem with Identity Provider";
×
92

93
    /**
94
     * We only have one field in which to store a unique
95
     * useridentifier/persistentuserid so we have to jam the the "entityId" for
96
     * a Shibboleth Identity Provider (IdP) and the unique persistent identifier
97
     * per user into the same field and a separator between these two would be
98
     * nice, in case we ever want to answer questions like "How many users
99
     * logged in from Harvard's Identity Provider?".
100
     * <p>
101
     * A pipe ("|") is used as a separator because it's considered "unwise" to
102
     * use in a URL and the "entityId" for a Shibboleth Identity Provider (IdP)
103
     * looks like a URL:
104
     * http://stackoverflow.com/questions/1547899/which-characters-make-a-url-invalid
105
     */
106
    private String persistentUserIdSeparator = "|";
×
107

108
    /**
109
     * The Shibboleth Identity Provider (IdP), an "entityId" which often but not
110
     * always looks like a URL.
111
     */
112
    String shibIdp;
113
    private String builtinUsername;
114
    private String builtinPassword;
115
    private String existingEmail;
116
    private String existingDisplayName;
117
    private boolean passwordRejected;
118
    private String displayNameToPersist;
119
    private String emailToPersist;
120
    private String affiliationToDisplayAtConfirmation = null;
×
121
    private String friendlyNameForInstitution = BundleUtil.getStringFromBundle("shib.welcomeExistingUserMessageDefaultInstitution");
×
122
    private State state;
123
    private String debugSummary;
124
    /**
125
     * After a successful login, we will redirect users to this page (unless
126
     * it's a new account).
127
     */
128
    private String redirectPage;
129
    //    private boolean debug = false;
130
    private String emailAddress;
131
    private Locale preferredNotificationsLanguage;
132

133
    public enum State {
×
134

135
        INIT,
×
136
        REGULAR_LOGIN_INTO_EXISTING_SHIB_ACCOUNT,
×
137
        PROMPT_TO_CREATE_NEW_ACCOUNT,
×
138
        PROMPT_TO_CONVERT_EXISTING_ACCOUNT,
×
139
    }
140

141
    public String init() {
142
        state = State.INIT;
×
143
        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
×
144
        request = (HttpServletRequest) context.getRequest();
×
145
        ShibUtil.printAttributes(request);
×
146

147
        /**
148
         * @todo Investigate why JkEnvVar is null since it may be useful for
149
         * debugging per https://github.com/IQSS/dataverse/issues/2916 . See
150
         * also
151
         * http://stackoverflow.com/questions/30193117/iterate-through-all-servletrequest-attributes#comment49933342_30193117
152
         * and
153
         * http://shibboleth.1660669.n2.nabble.com/Why-doesn-t-Java-s-request-getAttributeNames-show-Shibboleth-attributes-tp7616427p7616591.html
154
         */
155
        logger.debug("JkEnvVar: " + System.getenv("JkEnvVar"));
×
156

157
        shibService.possiblyMutateRequestInDev(request);
×
158

159
        try {
160
            shibIdp = getRequiredValueFromAssertion(ShibUtil.shibIdpAttribute);
×
161
        } catch (Exception ex) {
×
162
            /**
163
             * @todo is in an antipattern to throw exceptions to control flow?
164
             * http://c2.com/cgi/wiki?DontUseExceptionsForFlowControl
165
             *
166
             * All this exception handling should be handled in the new
167
             * ShibServiceBean so it's consistently handled by the API as well.
168
             */
169
            return StringUtils.EMPTY;
×
170
        }
×
171
        String shibUserIdentifier;
172
        try {
173
            shibUserIdentifier = getRequiredValueFromAssertion(ShibUtil.uniquePersistentIdentifier);
×
174
        } catch (Exception ex) {
×
175
            return StringUtils.EMPTY;
×
176
        }
×
177
        String firstName;
178
        try {
179
            firstName = getRequiredValueFromAssertion(ShibUtil.firstNameAttribute);
×
180
        } catch (Exception ex) {
×
181
            return StringUtils.EMPTY;
×
182
        }
×
183
        String lastName;
184
        try {
185
            lastName = getRequiredValueFromAssertion(ShibUtil.lastNameAttribute);
×
186
        } catch (Exception ex) {
×
187
            return StringUtils.EMPTY;
×
188
        }
×
189
        ShibUserNameFields shibUserNameFields = ShibUtil.findBestFirstAndLastName(firstName, lastName, null);
×
190
        if (shibUserNameFields != null) {
×
191
            String betterFirstName = shibUserNameFields.getFirstName();
×
192
            if (betterFirstName != null) {
×
193
                firstName = betterFirstName;
×
194
            }
195
            String betterLastName = shibUserNameFields.getLastName();
×
196
            if (betterLastName != null) {
×
197
                lastName = betterLastName;
×
198
            }
199
        }
200
        String emailAddressInAssertion = null;
×
201
        try {
202
            emailAddressInAssertion = getRequiredValueFromAssertion(ShibUtil.emailAttribute);
×
203
        } catch (Exception ex) {
×
204
            if (shibIdp.equals(ShibUtil.testShibIdpEntityId)) {
×
205
                logger.debug("For " + shibIdp + " (which as of this writing doesn't provide the " + ShibUtil.emailAttribute + " attribute) setting email address to value of eppn: " + shibUserIdentifier);
×
206
                emailAddressInAssertion = shibUserIdentifier;
×
207
            } else {
208
                // forcing all other IdPs to send us an an email
209
                return StringUtils.EMPTY;
×
210
            }
211
        }
×
212

213
        if (!EMailValidator.isEmailValid(emailAddressInAssertion, null)) {
×
214
            String msg = "The SAML assertion contained an invalid email address: \"" + emailAddressInAssertion + "\".";
×
215
            logger.info(msg);
×
216
            msg = BundleUtil.getStringFromBundle("shib.invalidEmailAddress", emailAddressInAssertion);
×
217
            String singleEmailAddress = ShibUtil.findSingleValue(emailAddressInAssertion);
×
218
            if (EMailValidator.isEmailValid(singleEmailAddress, null)) {
×
219
                msg = "Multiple email addresses were asserted by the Identity Provider (" + emailAddressInAssertion + " ). These were sorted and the first was chosen: " + singleEmailAddress;
×
220
                logger.info(msg);
×
221
                emailAddress = singleEmailAddress;
×
222
            } else {
223
                msg += BundleUtil.getStringFromBundle("shib.emailAddress.error");
×
224
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, identityProviderProblem, msg));
×
225
                return StringUtils.EMPTY;
×
226
            }
227
        } else {
×
228
            emailAddress = emailAddressInAssertion;
×
229
        }
230

231
        String usernameAssertion = getValueFromAssertion(ShibUtil.usernameAttribute);
×
232
        internalUserIdentifer = ShibUtil.generateFriendlyLookingUserIdentifer(usernameAssertion, emailAddress);
×
233
        logger.debug("friendly looking identifer (backend will enforce uniqueness):" + internalUserIdentifer);
×
234

235
        String affiliation = shibService.getAffiliation(shibIdp, shibService.getDevShibAccountType());
×
236
        if (affiliation != null) {
×
237
            affiliationToDisplayAtConfirmation = affiliation;
×
238
            friendlyNameForInstitution = affiliation;
×
239
        }
240
//        emailAddress = "willFailBeanValidation"; // for testing createAuthenticatedUser exceptions
241
        displayInfo = new AuthenticatedUserDisplayInfo(firstName, lastName, emailAddress, affiliation, null);
×
242

243
        userPersistentId = shibIdp + persistentUserIdSeparator + shibUserIdentifier;
×
244
        ShibAuthenticationProvider shibAuthProvider = new ShibAuthenticationProvider();
×
245
        AuthenticatedUser au = authSvc.lookupUser(shibAuthProvider.getId(), userPersistentId);
×
246
        if (au != null) {
×
247
            state = State.REGULAR_LOGIN_INTO_EXISTING_SHIB_ACCOUNT;
×
248
            logger.debug("Found user based on " + userPersistentId + ". Logging in.");
×
249

250
            if (!systemConfig.isReadonlyMode()) {
×
251
                logger.debug("Updating display info for " + au.getName());
×
252
                authSvc.updateAuthenticatedUser(au, displayInfo);
×
253
            } else {
254
                logger.warn("Can't update user display info in readonlyMode");
×
255
            }
256
            logInUserAndSetShibAttributes(au);
×
257
            String prettyFacesHomePageString = getPrettyFacesHomePageString(false);
×
258
            try {
259
                FacesContext.getCurrentInstance().getExternalContext().redirect(prettyFacesHomePageString);
×
260
            } catch (IOException ex) {
×
261
                logger.info("Unable to redirect user to homepage at " + prettyFacesHomePageString);
×
262
            }
×
263
        } else {
×
264
            state = State.PROMPT_TO_CREATE_NEW_ACCOUNT;
×
265
            displayNameToPersist = displayInfo.getTitle();
×
266
            emailToPersist = emailAddress;
×
267

268
            if (systemConfig.isReadonlyMode()) {
×
269
                return "/403.xhtml";
×
270
            }
271
            /**
272
             * @todo for Harvard we plan to use the value(s) from
273
             * eduPersonScopedAffiliation which
274
             * http://iam.harvard.edu/resources/saml-shibboleth-attributes says
275
             * can be One or more of the following values: faculty, staff,
276
             * student, affiliate, and member.
277
             *
278
             * http://dataverse.nl plans to use
279
             * urn:mace:dir:attribute-def:eduPersonAffiliation per
280
             * http://irclog.iq.harvard.edu/dataverse/2015-02-13#i_16265 . Can
281
             * they configure shibd to map eduPersonAffiliation to
282
             * eduPersonScopedAffiliation?
283
             */
284
//            positionToPersist = "FIXME";
285
            logger.debug("Couldn't find authenticated user based on " + userPersistentId);
×
286
            visibleTermsOfUse = true;
×
287
            /**
288
             * Using the email address from the IdP, try to find an existing
289
             * user. For TestShib we convert the "eppn" to an email address.
290
             *
291
             * If found, prompt for password and offer to convert.
292
             *
293
             * If not found, create a new account. It must be a new user.
294
             */
295
            String emailAddressToLookUp = emailAddress;
×
296
            if (existingEmail != null) {
×
297
                emailAddressToLookUp = existingEmail;
×
298
            }
299
            AuthenticatedUser existingAuthUserFoundByEmail = shibService.findAuthUserByEmail(emailAddressToLookUp);
×
300
            BuiltinUser existingBuiltInUserFoundByEmail = null;
×
301
            if (existingAuthUserFoundByEmail != null) {
×
302
                existingDisplayName = existingAuthUserFoundByEmail.getName();
×
303
                existingBuiltInUserFoundByEmail = shibService.findBuiltInUserByAuthUserIdentifier(existingAuthUserFoundByEmail.getUserIdentifier());
×
304
                if (existingBuiltInUserFoundByEmail != null) {
×
305
                    state = State.PROMPT_TO_CONVERT_EXISTING_ACCOUNT;
×
306

307
                    debugSummary = "getting username from the builtin user we looked up via email";
×
308
                    builtinUsername = existingBuiltInUserFoundByEmail.getUserName();
×
309
                } else {
310
                    debugSummary = "Could not find a builtin account based on the username. Here we should simply create a new Shibboleth user";
×
311
                }
312
            } else {
313
                debugSummary = "Could not find an auth user based on email address";
×
314
            }
315
            consents = consentService.prepareConsentsForView(session.getLocale());
×
316

317
        }
318
        logger.debug("Debug summary: " + debugSummary + " (state: " + state + ").");
×
319
        logger.debug("redirectPage: " + redirectPage);
×
320
        return StringUtils.EMPTY;
×
321
    }
322

323
    public String confirmAndCreateAccount() {
324
        ShibAuthenticationProvider shibAuthProvider = new ShibAuthenticationProvider();
×
325
        String lookupStringPerAuthProvider = userPersistentId;
×
326
        AuthenticatedUser au = null;
×
327
        try {
328
            au = authSvc.createAuthenticatedUser(
×
329
                    new UserRecordIdentifier(shibAuthProvider.getId(), lookupStringPerAuthProvider), internalUserIdentifer, displayInfo, true);
×
330
        } catch (EJBException ex) {
×
331
            /**
332
             * @todo Show the ConstraintViolationException, if any.
333
             */
334
            logger.info("Couldn't create user " + userPersistentId + " due to exception: " + ex.getCause());
×
335
        }
×
336
        if (au != null) {
×
337
            logger.debug("created user " + au.getIdentifier());
×
338
            logInUserAndSetShibAttributes(au);
×
339
            /**
340
             * @todo Move this to
341
             * AuthenticationServiceBean.createAuthenticatedUser
342
             */
343
            userNotificationService.sendNotification(au,
×
344
                                                     new Timestamp(new Date().getTime()),
×
345
                                                     NotificationType.CREATEACC);
346

347
            consentService.executeActionsAndSaveAcceptedConsents(consents, au);
×
348
            return "/dataverseuser.xhtml?selectTab=accountInfo&faces-redirect=true";
×
349
        } else {
350
            JsfHelper.addFlashErrorMessage(BundleUtil.getStringFromBundle("shib.createUser.fail"));
×
351
        }
352
        return getPrettyFacesHomePageString(true);
×
353
    }
354

355
    public String confirmAndConvertAccount() {
356
        visibleTermsOfUse = false;
×
357
        ShibAuthenticationProvider shibAuthProvider = new ShibAuthenticationProvider();
×
358
        String lookupStringPerAuthProvider = userPersistentId;
×
359
        logger.debug("builtin username: " + builtinUsername);
×
360
        AuthenticatedUser builtInUserToConvert = authSvc.canLogInAsBuiltinUser(builtinUsername, builtinPassword);
×
361
        if (builtInUserToConvert != null) {
×
362
            // TODO: Switch from authSvc.convertBuiltInToShib to authSvc.convertBuiltInUserToRemoteUser
363
            AuthenticatedUser au = authSvc.convertBuiltInUserToRemoteUser(builtInUserToConvert, shibAuthProvider.getId(), lookupStringPerAuthProvider);
×
364
            if (au != null) {
×
365
                authSvc.updateAuthenticatedUser(au, displayInfo);
×
366
                logInUserAndSetShibAttributes(au);
×
367
                consentService.executeActionsAndSaveAcceptedConsents(consents, au);
×
368
                debugSummary = "Local account validated and successfully converted to a Shibboleth account. The old account username was " + builtinUsername;
×
369
                JsfHelper.addFlashSuccessMessage(BundleUtil.getStringFromBundle("dataverse.shib.success"));
×
370
                return "/dataverseuser.xhtml?selectTab=accountInfo&faces-redirect=true";
×
371
            } else {
372
                debugSummary = "Local account validated but unable to convert to Shibboleth account.";
×
373
            }
374
        } else {
×
375
            passwordRejected = true;
×
376
            debugSummary = "Username/password combination for local account was invalid";
×
377
        }
378
        return null;
×
379
    }
380

381
    private void logInUserAndSetShibAttributes(AuthenticatedUser au) {
NEW
382
        au.setShibIdentityProvider(shibIdp);
×
383
        session.setUser(au);
×
384
        logger.debug("Groups for user " + au.getId() + " (" + au.getIdentifier() + "): " + getGroups(au));
×
385
    }
×
386

387
    public List<String> getGroups(AuthenticatedUser au) {
388
        List<String> groups = new ArrayList<>();
×
389
        groupService.groupsFor(au, null).stream().forEach((group) -> {
×
390
            groups.add(group.getDisplayName() + " (" + group.getIdentifier() + ")");
×
391
        });
×
392
        return groups;
×
393
    }
394

395
    /**
396
     * @todo The mockups show a Cancel button but because we're using the
397
     * "requiredCheckboxValidator" you are forced to agree to Terms of Use
398
     * before clicking Cancel! Argh! The mockups show how we want to display
399
     * Terms of Use in a popup anyway so this should all be re-done. No time
400
     * now. Here's the mockup:
401
     * https://iqssharvard.mybalsamiq.com/projects/loginwithshibboleth-version3-dataverse40/Dataverse%20Account%20III%20-%20Agree%20Terms%20of%20Use
402
     */
403
    public String cancel() {
404
        return loginpage + "?faces-redirect=true";
×
405
    }
406

407
    /**
408
     * @return The trimmed value of a Shib attribute (if non-empty) or null.
409
     * @todo Move this to ShibUtil
410
     */
411
    private String getValueFromAssertion(String key) {
412
        Object attribute = request.getAttribute(key);
×
413
        if (attribute != null) {
×
414
            String attributeValue = attribute.toString();
×
415
            if(settingsService.isTrueForKey(Key.ShibAttributeCharacterSetConversionEnabled)) {
×
416
                attributeValue = new String(attributeValue.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
×
417
            }
418
            String trimmedValue = attributeValue.trim();
×
419
            if (!trimmedValue.isEmpty()) {
×
420
                logger.debug("The SAML assertion for \"" + key + "\" (optional) was \"" + attributeValue + "\" and was trimmed to \"" + trimmedValue + "\".");
×
421
                return trimmedValue;
×
422
            } else {
423
                logger.debug("The SAML assertion for \"" + key + "\" (optional) was \"" + attributeValue + "\" and was trimmed to \"" + trimmedValue + "\" (empty string). Returing null.");
×
424
                return null;
×
425
            }
426
        } else {
427
            logger.debug("The SAML assertion for \"" + key + "\" (optional) was null.");
×
428
            return null;
×
429
        }
430
    }
431

432
    /**
433
     * @return The trimmed value of a Shib attribute (if non-empty) or null.
434
     * @todo Move this to ShibUtil. More objects might be required since
435
     * sometimes we want to show messages, etc.
436
     */
437
    private String getRequiredValueFromAssertion(String key) throws Exception {
438
        Object attribute = request.getAttribute(key);
×
439
        if (attribute == null) {
×
440
            String msg = "The SAML assertion for \"" + key + "\" was null. Please contact support.";
×
441
            logger.info(msg);
×
442
            boolean showMessage = true;
×
443
            if (shibIdp.equals(ShibUtil.testShibIdpEntityId) && key.equals(ShibUtil.emailAttribute)) {
×
444
                showMessage = false;
×
445
            }
446
            if (showMessage) {
×
447
                FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, identityProviderProblem, BundleUtil.getStringFromBundle("shib.nullerror", key)));
×
448
            }
449
            throw new Exception(msg);
×
450
        }
451
        String attributeValue = attribute.toString();
×
452
        if (attributeValue.isEmpty()) {
×
453
            throw new Exception(key + " was empty");
×
454
        }
455
        if(settingsService.isTrueForKey(Key.ShibAttributeCharacterSetConversionEnabled)) {
×
456
            attributeValue = new String(attributeValue.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
×
457
        }
458
        String trimmedValue = attributeValue.trim();
×
459
        logger.debug("The SAML assertion for \"" + key + "\" (required) was \"" + attributeValue + "\" and was trimmed to \"" + trimmedValue + "\".");
×
460
        return trimmedValue;
×
461
    }
462

463
    public String getRootDataverseAlias() {
464
        Dataverse rootDataverse = dataverseDao.findRootDataverse();
×
465
        if (rootDataverse != null) {
×
466
            String rootDvAlias = rootDataverse.getAlias();
×
467
            return rootDvAlias;
×
468
        }
469
        return null;
×
470
    }
471

472
    /**
473
     * @param includeFacetDashRedirect if true, include "faces-redirect=true" in
474
     *                                 the string
475
     * @todo Once https://github.com/IQSS/dataverse/issues/1519 is done, revisit
476
     * this method and have the home page be "/" rather than "/dataverses/root".
477
     * @todo Like builtin users, Shibboleth should benefit from redirectPage
478
     * logic per https://github.com/IQSS/dataverse/issues/1551
479
     */
480
    public String getPrettyFacesHomePageString(boolean includeFacetDashRedirect) {
481
        if (redirectPage != null) {
×
482
            return redirectPage;
×
483
        }
484
        String plainHomepageString = "/dataverse.xhtml";
×
485
        String rootDvAlias = getRootDataverseAlias();
×
486
        if (includeFacetDashRedirect) {
×
487
            if (rootDvAlias != null) {
×
488
                return plainHomepageString + "?alias=" + rootDvAlias + "&faces-redirect=true";
×
489
            } else {
490
                return plainHomepageString + "?faces-redirect=true";
×
491
            }
492
        } else if (rootDvAlias != null) {
×
493
            /**
494
             * @todo Is there a constant for "/dataverse/" anywhere? I guess
495
             * we'll just hard-code it here.
496
             */
497
            return "/dataverse/" + rootDvAlias;
×
498
        } else {
499
            return plainHomepageString;
×
500
        }
501
    }
502

503
    public boolean isInit() {
504
        return state.equals(State.INIT);
×
505
    }
506

507
    public boolean isOfferToCreateNewAccount() {
508
        return state.equals(State.PROMPT_TO_CREATE_NEW_ACCOUNT);
×
509
    }
510

511
    public boolean isOfferToConvertExistingAccount() {
512
        return state.equals(State.PROMPT_TO_CONVERT_EXISTING_ACCOUNT);
×
513
    }
514

515
    public String getDisplayNameToPersist() {
516
        return displayNameToPersist;
×
517
    }
518

519
    public String getEmailToPersist() {
520
        return emailToPersist;
×
521
    }
522

523
    public String getAffiliationToDisplayAtConfirmation() {
524
        return affiliationToDisplayAtConfirmation;
×
525
    }
526

527
    public String getExistingEmail() {
528
        return existingEmail;
×
529
    }
530

531
    public void setExistingEmail(String existingEmail) {
532
        this.existingEmail = existingEmail;
×
533
    }
×
534

535
    public String getExistingDisplayName() {
536
        return existingDisplayName;
×
537
    }
538

539
    public boolean isPasswordRejected() {
540
        return passwordRejected;
×
541
    }
542

543
    public String getFriendlyNameForInstitution() {
544
        return friendlyNameForInstitution;
×
545
    }
546

547
    public void setFriendlyNameForInstitution(String friendlyNameForInstitution) {
548
        this.friendlyNameForInstitution = friendlyNameForInstitution;
×
549
    }
×
550

551
    public State getState() {
552
        return state;
×
553
    }
554

555
    public boolean isVisibleTermsOfUse() {
556
        return visibleTermsOfUse;
×
557
    }
558

559
    public String getBuiltinUsername() {
560
        return builtinUsername;
×
561
    }
562

563
    public void setBuiltinUsername(String builtinUsername) {
564
        this.builtinUsername = builtinUsername;
×
565
    }
×
566

567
    public String getBuiltinPassword() {
568
        return builtinPassword;
×
569
    }
570

571
    public void setBuiltinPassword(String builtinPassword) {
572
        this.builtinPassword = builtinPassword;
×
573
    }
×
574

575
    public String getDebugSummary() {
576
        return debugSummary;
×
577
    }
578

579
    public void setDebugSummary(String debugSummary) {
580
        this.debugSummary = debugSummary;
×
581
    }
×
582

583
    public String getRedirectPage() {
584
        return redirectPage;
×
585
    }
586

587
    public void setRedirectPage(String redirectPage) {
588
        this.redirectPage = redirectPage;
×
589
    }
×
590

591
    public List<String> getSupportedLanguages() {
592
        return new ArrayList<>(settingsWrapper.getConfiguredLocales().keySet());
×
593
    }
594

595
    public String getPreferredNotificationsLanguage() {
596
        return Option.of(preferredNotificationsLanguage)
×
597
                .map(locale -> locale.getLanguage())
×
598
                .getOrNull();
×
599
    }
600

601
    public String getLocalizedPreferredNotificationsLanguage() {
602
        return getLocalizedDisplayNameForLanguage(preferredNotificationsLanguage);
×
603
    }
604

605
    public String getLocalizedDisplayNameForLanguage(String language) {
606
        return getLocalizedDisplayNameForLanguage(Locale.forLanguageTag(language));
×
607
    }
608

609
    public List<ConsentDto> getConsents() {
610
        return consents;
×
611
    }
612

613
    public void validatePreferredNotificationsLanguage(FacesContext context, UIComponent toValidate, Object value) {
614
        if(Objects.isNull(value)) {
×
615
            ((UIInput) toValidate).setValid(false);
×
616
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, BundleUtil.getStringFromBundle("user.notificationsLanguage.requiredMessage"), null);
×
617
            context.addMessage(toValidate.getClientId(context), message);
×
618
        }
619
    }
×
620

621
    // -------------------- PRIVATE ---------------------
622

623
    private String getLocalizedDisplayNameForLanguage(Locale language) {
624
        return language.getDisplayName(session.getLocale());
×
625
    }
626

627
    // -------------------- SETTERS --------------------
628

629
    public void setPreferredNotificationsLanguage(String preferredNotificationsLanguage) {
630
        this.preferredNotificationsLanguage = Locale.forLanguageTag(preferredNotificationsLanguage);
×
631
    }
×
632

633
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc