JSF 2.3

4 min read
package org.example.kickoff.config;

import static java.text.MessageFormat.format;
import static java.util.ResourceBundle.getBundle;
import static org.example.kickoff.model.Group.ADMIN;
import static org.omnifaces.util.Faces.getLocale;
import static org.omnifaces.utils.Lang.isEmpty;

import java.util.ResourceBundle;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.example.kickoff.business.service.UserService;
import org.example.kickoff.model.User;
import org.omnifaces.cdi.Startup;
import org.omnifaces.util.Messages;

@Startup
public class StartupBean {

	@Inject
	private UserService userService;

	@PostConstruct
	public void init() {
		setupTestUsers();
		configureMessageResolver();
	}

	private void setupTestUsers() {
		if (!userService.findByEmail("admin@kickoff.example.org").isPresent()) {
			User user = new User();
			user.setFirstName("Test");
			user.setLastName("Admin");
			user.setEmail("admin@kickoff.example.org");
			userService.register(user, "passw0rd", ADMIN);
		}

		if (!userService.findByEmail("user@kickoff.example.org").isPresent()) {
			User user = new User();
			user.setFirstName("Test");
			user.setLastName("User");
			user.setEmail("user@kickoff.example.org");
			userService.register(user, "passw0rd");
		}
	}

	private void configureMessageResolver() {
		Messages.setResolver(new Messages.Resolver() {
			private static final String BASE_NAME = "org.example.kickoff.i18n.ApplicationBundle";

			@Override
			public String getMessage(String message, Object... params) {
				ResourceBundle bundle = getBundle(BASE_NAME, getLocale());
				if (bundle.containsKey(message)) {
					message = bundle.getString(message);
				}
				return isEmpty(params) ? message : format(message, params);
			}
		});
	}

}
package org.example.kickoff.config;
import java.time.Instant;
import java.util.Date;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

/**
 * Converts the JDK 8 / JSR 310 <code>Instant</code> type to <code>Date</code> for
 * usage with JPA.
 * 
 * <p>
 * This converter is still necessary for Java EE 8 / JPA 2.2, since Instant is one of the
 * "forgotten" types.
 * 
 * See https://github.com/eclipse-ee4j/jpa-api/issues/163
 * 
 * @author Arjan Tijms
 */
@Converter(autoApply = true)
public class InstantConverter implements AttributeConverter<Instant, Date> {

	@Override
	public Date convertToDatabaseColumn(Instant instant) {
		if (instant == null) {
			return null;
		}
		
		return Date.from(instant);
	}

	@Override
	public Instant convertToEntityAttribute(Date date) {
		if (date == null) {
			return null;
		}
		
		return date.toInstant();
	}
}
package org.example.kickoff.config;

import static java.util.logging.Logger.getLogger;
import static org.omnifaces.utils.properties.PropertiesUtils.loadPropertiesFromClasspath;
import static org.omnifaces.utils.properties.PropertiesUtils.loadXMLPropertiesStagedFromClassPath;

import java.util.Map;
import java.util.logging.Logger;

import org.omnifaces.persistence.datasource.PropertiesFileLoader;

/**
 * Adaptor for the switchable datasource as defined in web.xml to be able to read properties from the
 * right file.
 */
public class DataSourceStagedPropertiesFileLoader implements PropertiesFileLoader {

	private static final Logger logger = getLogger(DataSourceStagedPropertiesFileLoader.class.getName());

	@Override
	public Map<String, String> loadFromFile(String fileName) {

		// Make sure we use the same names as the application settings are using
		Map<String, String> omniSettings = loadPropertiesFromClasspath("META-INF/omni-settings");

		Map<String, String> dataSourceProperties = loadXMLPropertiesStagedFromClassPath(
			fileName,
			omniSettings.getOrDefault("stageSystemPropertyName", "omni.stage"),
			omniSettings.get("defaultStage"));

		logger.info(
			"\n\nAbout to install DataSource. \n" +
			"Classname: " + dataSourceProperties.get("className") + "\n" +
			"URL: " + dataSourceProperties.getOrDefault("url", dataSourceProperties.get("URL") + "\n" +
			"See META-INF/conf/" + fileName + " for details. \n" +
			"\n\n")
		);

		return dataSourceProperties;

	}

}

/src/main/java/org/example/kickoff/config/auth/

package org.example.kickoff.config.auth;

import javax.security.enterprise.CallerPrincipal;

import org.example.kickoff.model.User;

/**
 * @see KickoffIdentityStore
 * @see ActiveUser
 */
public class KickoffCallerPrincipal extends CallerPrincipal {

	private final User user;

	public KickoffCallerPrincipal(User user) {
		super(user.getEmail());
		this.user = user;
	}

	public User getUser() {
		return user;
	}

}
package org.example.kickoff.config.auth;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.security.enterprise.AuthenticationStatus;
import javax.security.enterprise.authentication.mechanism.http.AutoApplySession;
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import javax.security.enterprise.authentication.mechanism.http.LoginToContinue;
import javax.security.enterprise.authentication.mechanism.http.RememberMe;
import javax.security.enterprise.credential.Credential;
import javax.security.enterprise.identitystore.IdentityStoreHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


/**
 * Authentication mechanism that authenticates according to the Servlet spec defined FORM authentication mechanism.
 * See Servlet spec for further details.
 *
 * @author Arjan Tijms
 */
@AutoApplySession // For "Is user already logged-in?"
@RememberMe(
		cookieSecureOnly = false, // Remove this when login is served over HTTPS.
		cookieMaxAgeSeconds = 60 * 60 * 24 * 14) // 14 days.
@LoginToContinue(
		loginPage = "/login?continue=true",
		errorPage = "",
		useForwardToLogin = false)
@ApplicationScoped
public class KickoffFormAuthenticationMechanism implements HttpAuthenticationMechanism {

    @Inject
    private IdentityStoreHandler identityStoreHandler;

	@Override
	public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) {
		Credential credential = context.getAuthParameters().getCredential();

		if (credential != null) {
            return context.notifyContainerAboutLogin(identityStoreHandler.validate(credential));
        }
		else {
			return context.doNothing();
		}
	}

}
package org.example.kickoff.config.auth;

import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
import static javax.security.enterprise.identitystore.CredentialValidationResult.NOT_VALIDATED_RESULT;
import static org.example.kickoff.model.Group.USER;

import java.util.function.Supplier;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.security.enterprise.credential.CallerOnlyCredential;
import javax.security.enterprise.credential.Credential;
import javax.security.enterprise.credential.UsernamePasswordCredential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;

import org.example.kickoff.business.exception.EmailNotVerifiedException;
import org.example.kickoff.business.exception.InvalidGroupException;
import org.example.kickoff.business.exception.CredentialsException;
import org.example.kickoff.business.service.UserService;
import org.example.kickoff.model.User;

@ApplicationScoped
public class KickoffIdentityStore implements IdentityStore {

	@Inject
	private UserService userService;

	@Override
	public CredentialValidationResult validate(Credential credential) {
		Supplier<User> userSupplier = null;

		if (credential instanceof UsernamePasswordCredential) {
			String email = ((UsernamePasswordCredential) credential).getCaller();
			String password = ((UsernamePasswordCredential) credential).getPasswordAsString();
			userSupplier = () -> userService.getByEmailAndPassword(email, password);
		}
		else if (credential instanceof CallerOnlyCredential) {
			String email = ((CallerOnlyCredential) credential).getCaller();
			userSupplier = () -> userService.getByEmail(email);
		}

		return validate(userSupplier);
	}

	static CredentialValidationResult validate(Supplier<User> userSupplier) {
		if (userSupplier == null) {
			return NOT_VALIDATED_RESULT;
		}

		try {
			User user = userSupplier.get();

			if (!user.getGroups().contains(USER)) {
				throw new InvalidGroupException();
			}

			if (!user.isEmailVerified()) {
				throw new EmailNotVerifiedException();
			}

			return new CredentialValidationResult(new KickoffCallerPrincipal(user), user.getRolesAsStrings());
		}
		catch (CredentialsException e) {
			return INVALID_RESULT;
		}
	}

}
package org.example.kickoff.config.auth;

import static org.example.kickoff.model.LoginToken.TokenType.REMEMBER_ME;
import static org.omnifaces.util.Servlets.getRemoteAddr;

import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.security.enterprise.CallerPrincipal;
import javax.security.enterprise.credential.RememberMeCredential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.RememberMeIdentityStore;
import javax.servlet.http.HttpServletRequest;

import org.example.kickoff.business.service.LoginTokenService;
import org.example.kickoff.business.service.UserService;

@ApplicationScoped
public class KickoffRememberMeIdentityStore implements RememberMeIdentityStore {

	@Inject
	private HttpServletRequest request;

	@Inject
	private UserService userService;

	@Inject
	private LoginTokenService loginTokenService;

	@Override
	public CredentialValidationResult validate(RememberMeCredential credential) {
		return KickoffIdentityStore.validate(() -> userService.getByLoginToken(credential.getToken(), REMEMBER_ME));
	}

	@Override
	public String generateLoginToken(CallerPrincipal callerPrincipal, Set<String> groups) {
		String ipAddress = getRemoteAddr(request);
		String description = "Remember me session for " + ipAddress + " on " + request.getHeader("User-Agent");
		return loginTokenService.generate(callerPrincipal.getName(), ipAddress, description, REMEMBER_ME);
	}

	@Override
	public void removeLoginToken(String loginToken) {
		loginTokenService.remove(loginToken);
	}

}
Orestis Pantazos

Orestis Pantazos

DevOps Engineer