Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FISH-384: Do not activate if definition-bearing bean is disabled #80

Merged
merged 2 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
FISH-384: Do not activate if definition-bearing bean is disabled
  • Loading branch information
pdudits committed Apr 14, 2021
commit cae5e8792b1cf8fc6447a7a92f8a36c3644e4bdf
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,15 @@
*/
package fish.payara.security.openid;

import static fish.payara.security.openid.OpenIdUtil.isEmpty;
import static fish.payara.security.openid.api.OpenIdConstant.ERROR_DESCRIPTION_PARAM;
import static fish.payara.security.openid.api.OpenIdConstant.ERROR_PARAM;
import static fish.payara.security.openid.api.OpenIdConstant.EXPIRES_IN;
import static fish.payara.security.openid.api.OpenIdConstant.REFRESH_TOKEN;
import static fish.payara.security.openid.api.OpenIdConstant.STATE;
import static fish.payara.security.openid.api.OpenIdConstant.TOKEN_TYPE;

import fish.payara.security.openid.api.AccessTokenCredential;
import fish.payara.security.openid.api.OpenIdState;
import fish.payara.security.openid.api.RefreshToken;
import fish.payara.security.openid.controller.AuthenticationController;
import fish.payara.security.openid.controller.StateController;
import fish.payara.security.openid.controller.TokenController;
import fish.payara.security.openid.domain.LogoutConfiguration;
import fish.payara.security.openid.domain.OpenIdConfiguration;
import fish.payara.security.openid.domain.OpenIdContextImpl;
import fish.payara.security.openid.domain.RefreshTokenImpl;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import java.util.Optional;
import java.util.logging.Level;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import java.util.logging.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.enterprise.inject.Typed;
import javax.inject.Inject;
import javax.json.Json;
import javax.json.JsonNumber;
Expand All @@ -78,20 +56,43 @@
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.enterprise.AuthenticationException;
import javax.security.enterprise.AuthenticationStatus;
import static javax.security.enterprise.AuthenticationStatus.SEND_FAILURE;
import static javax.security.enterprise.AuthenticationStatus.SUCCESS;
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
import static javax.security.enterprise.identitystore.CredentialValidationResult.NOT_VALIDATED_RESULT;
import javax.security.enterprise.identitystore.IdentityStoreHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

import fish.payara.security.openid.api.AccessTokenCredential;
import fish.payara.security.openid.api.OpenIdState;
import fish.payara.security.openid.api.RefreshToken;
import fish.payara.security.openid.controller.AuthenticationController;
import fish.payara.security.openid.controller.StateController;
import fish.payara.security.openid.controller.TokenController;
import fish.payara.security.openid.domain.LogoutConfiguration;
import fish.payara.security.openid.domain.OpenIdConfiguration;
import fish.payara.security.openid.domain.OpenIdContextImpl;
import fish.payara.security.openid.domain.RefreshTokenImpl;

import static fish.payara.security.openid.OpenIdUtil.isEmpty;
import static fish.payara.security.openid.api.OpenIdConstant.ERROR_DESCRIPTION_PARAM;
import static fish.payara.security.openid.api.OpenIdConstant.ERROR_PARAM;
import static fish.payara.security.openid.api.OpenIdConstant.EXPIRES_IN;
import static fish.payara.security.openid.api.OpenIdConstant.REFRESH_TOKEN;
import static fish.payara.security.openid.api.OpenIdConstant.STATE;
import static fish.payara.security.openid.api.OpenIdConstant.TOKEN_TYPE;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static javax.security.enterprise.AuthenticationStatus.SEND_FAILURE;
import static javax.security.enterprise.AuthenticationStatus.SUCCESS;
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
import static javax.security.enterprise.identitystore.CredentialValidationResult.NOT_VALIDATED_RESULT;

/**
* The AuthenticationMechanism used to authenticate users using the OpenId
* Connect protocol
Expand Down Expand Up @@ -126,8 +127,8 @@
// | |<------------------------------------------------------| |
// | | | |
// +--------+ +--------+
@Alternative
@ApplicationScoped
@Typed(OpenIdAuthenticationMechanism.class)
public class OpenIdAuthenticationMechanism implements HttpAuthenticationMechanism {

public static final String BEARER_PREFIX = "Bearer ";
Expand Down
100 changes: 61 additions & 39 deletions openid/src/main/java/fish/payara/security/openid/OpenIdExtension.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,24 @@
import fish.payara.security.annotations.OpenIdAuthenticationDefinition;
import fish.payara.security.openid.controller.AuthenticationController;
import fish.payara.security.openid.controller.ConfigurationController;
import fish.payara.security.openid.controller.JWTValidator;
import fish.payara.security.openid.controller.NonceController;
import fish.payara.security.openid.controller.ProviderMetadataContoller;
import fish.payara.security.openid.controller.StateController;
import fish.payara.security.openid.controller.TokenController;
import fish.payara.security.openid.controller.UserInfoController;
import fish.payara.security.openid.domain.OpenIdConfiguration;
import fish.payara.security.openid.domain.OpenIdContextImpl;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterTypeDiscovery;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.DefinitionException;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.WithAnnotations;
import javax.enterprise.inject.spi.*;
import javax.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import javax.security.enterprise.identitystore.IdentityStore;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import static java.util.logging.Level.INFO;
Expand All @@ -75,10 +76,29 @@ public class OpenIdExtension implements Extension {
private static final Logger LOGGER = Logger.getLogger(OpenIdExtension.class.getName());

private OpenIdAuthenticationDefinition definition;
private boolean deployedAsAppLibrary;
private Class<?> definitionSource;
private boolean definitionActive;

protected void registerTypes(@Observes BeforeBeanDiscovery before) {
registerTypes(before,
AuthenticationController.class,
ConfigurationController.class,
NonceController.class,
ProviderMetadataContoller.class,
StateController.class,
TokenController.class,
UserInfoController.class,
OpenIdContextImpl.class,
OpenIdIdentityStore.class,
OpenIdAuthenticationMechanism.class,
JWTValidator.class
);
}

protected void foundMyClasses(@Observes ProcessAnnotatedType<OpenIdContextImpl> myType) {
this.deployedAsAppLibrary = true;
private void registerTypes(BeforeBeanDiscovery event, Class<?>... classes) {
for (Class<?> aClass : classes) {
event.addAnnotatedType(aClass, aClass.getName());
}
}

/**
Expand All @@ -95,7 +115,8 @@ private void setDefinition(OpenIdAuthenticationDefinition definition, Class<?> s
LOGGER.warning("Multiple authentication definition found. Will ignore the definition in " + sourceClass);
return;
}

validateExtraParametersFormat(definition);
this.definitionSource = sourceClass;
this.definition = definition;
LOGGER.log(INFO, "Activating {0} OpenID Connect authentication definition from class {1}",
new Object[]{definitionKind, sourceClass.getName()});
Expand Down Expand Up @@ -138,44 +159,45 @@ protected void validateExtraParametersFormat(OpenIdAuthenticationDefinition defi
}
}

protected void afterTypeDiscovery(@Observes AfterTypeDiscovery afterTypeDiscovery) {
if (!deployedAsAppLibrary) {
registerTypes(afterTypeDiscovery);
}
if (this.definition != null) {
// if there is a definition, enable mechanism and identity store
afterTypeDiscovery.getAlternatives().add(OpenIdAuthenticationMechanism.class);
afterTypeDiscovery.getAlternatives().add(OpenIdIdentityStore.class);
protected void watchActiveBeans(@Observes ProcessBean<?> processBean) {
if (definitionSource != null && definitionSource.equals(processBean.getAnnotated().getBaseType())) {
definitionActive = true;
}
}

protected void registerTypes(AfterTypeDiscovery event) {
// in case this is bundled in server and not a library, the types needs explicit registration
// in such case they need explicit type id, otherwise this type conflicts with type discovered within
// extension bean manager.
registerType(event, OpenIdContextImpl.class);
registerType(event, NonceController.class);
registerType(event, StateController.class);
registerType(event, ConfigurationController.class);
registerType(event, ProviderMetadataContoller.class);
registerType(event, AuthenticationController.class);
registerType(event, TokenController.class);
registerType(event, UserInfoController.class);
registerType(event, OpenIdAuthenticationMechanism.class);
}
protected void registerDefinition(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {

private void registerType(AfterTypeDiscovery event, Class<?> type) {
event.addAnnotatedType(type, "OIDCExtension/" + type.getName());
}
if (definitionActive) {
// if definition is active we broaden the type of OpenIdAuthenticationMechanism back to
// HttpAuthenticationMechanism, so it would be picked up by Jakarta Security.
afterBeanDiscovery.addBean()
.beanClass(HttpAuthenticationMechanism.class)
.addType(HttpAuthenticationMechanism.class)
.scope(ApplicationScoped.class)
.produceWith(in -> in.select(OpenIdAuthenticationMechanism.class).get());

afterBeanDiscovery.addBean()
.beanClass(IdentityStore.class)
.addType(IdentityStore.class)
.scope(ApplicationScoped.class)
.produceWith(in -> in.select(OpenIdIdentityStore.class));

protected void registerDefinition(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
if (definition != null) {
afterBeanDiscovery.addBean()
.beanClass(OpenIdAuthenticationDefinition.class)
.types(OpenIdAuthenticationDefinition.class)
.scope(ApplicationScoped.class)
.id("OpenId Definition")
.createWith(cc -> this.definition);
} else {
// Publish empty definition to prevent injection errors. The helper components will not work, but
// will not cause definition error. This is quite unlucky situation, but when definition is on an
// alternative bean we don't know before this moment whether the bean is enabled or not.
afterBeanDiscovery.addBean()
.beanClass(OpenIdAuthenticationDefinition.class)
.types(OpenIdAuthenticationDefinition.class)
.scope(Dependent.class)
.id("Null OpenId Definition")
.createWith(cc -> null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@
*/
package fish.payara.security.openid;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;

import com.nimbusds.jose.Algorithm;
import com.nimbusds.jwt.JWTClaimsSet;
import fish.payara.security.openid.api.OpenIdConstant;
Expand All @@ -46,28 +57,15 @@
import fish.payara.security.openid.domain.OpenIdConfiguration;
import fish.payara.security.openid.domain.OpenIdContextImpl;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import java.util.Set;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import javax.inject.Inject;
import javax.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;

/**
* Identity store validates the identity token & access toekn and returns the
* validation result with the caller name and groups.
*
* @author Gaurav Gupta
*/
@Alternative
@ApplicationScoped
public class OpenIdIdentityStore implements IdentityStore {

Expand Down
45 changes: 0 additions & 45 deletions openid/src/main/resources/META-INF/beans.xml

This file was deleted.