All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.strimzi.kafka.oauth.server.OAuthSessionAuthorizer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017-2020, Strimzi authors.
 * License: Apache License 2.0 (see the file LICENSE or http://apache.org/licenses/LICENSE-2.0.html).
 */
package io.strimzi.kafka.oauth.server;

import io.strimzi.kafka.oauth.common.BearerTokenWithPayload;
import io.strimzi.kafka.oauth.common.Config;
import io.strimzi.kafka.oauth.common.ConfigException;
import io.strimzi.kafka.oauth.common.TimeUtil;
import org.apache.kafka.common.Endpoint;
import org.apache.kafka.common.acl.AclBinding;
import org.apache.kafka.common.acl.AclBindingFilter;
import org.apache.kafka.common.security.auth.KafkaPrincipal;
import org.apache.kafka.server.authorizer.AclCreateResult;
import org.apache.kafka.server.authorizer.AclDeleteResult;
import org.apache.kafka.server.authorizer.Action;
import org.apache.kafka.server.authorizer.AuthorizableRequestContext;
import org.apache.kafka.server.authorizer.AuthorizationResult;
import org.apache.kafka.server.authorizer.Authorizer;
import org.apache.kafka.server.authorizer.AuthorizerServerInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletionStage;

import static io.strimzi.kafka.oauth.common.LogUtil.mask;

/**
 * An authorizer that grants access only if the access token used during SASL/OAUTHBEARER based authentication
 * has not yet expired based on expiry time of the token as set by Authorization Server when the token was issued.
 * 

* This authorizer does not detect if the token was invalidated mid-session by explicitly revoking it at the * authorization server or by revoking the JWKS signing keys at the authorization server. *

* To install this authorizer in Kafka broker, specify the following in your 'server.properties': *

 *     authorizer.class.name=io.strimzi.kafka.oauth.server.OAuthSessionAuthorizer
 *     principal.builder.class=io.strimzi.kafka.oauth.server.OAuthKafkaPrincipalBuilder
 * 
* When you would otherwise configure another authorizer, you'll need to specify that other authorizer as the delegate * authorizer using the strimzi.authorizer.delegate.class.name configuration property. * For example: *
 *     strimzi.authorizer.delegate.class.name=kafka.security.authorizer.AclAuthorizer
 * 
* The specified delegate authorizer should be configured according to its documentation, as if it was installed as the * main authorizer (using authorizer.class.name). *

* If the `strimzi.authorizer.delegate.class.name` is not set, then you need to explicitly specify that all authorizations * should be granted, as long as the access token has not expired by specifying: *

 *     strimzi.authorizer.grant.when.no.delegate=true
 * 
* With this setting the OAuthSessionAuthorizer behaves as if there was no authorizer installed - it grants everything with * the exception that the sessions using SASL/OAUTHBEARER with expired token will be denied. *

* This authorizer doesn't take super.users setting into account. When used without a delegate every user effectively becomes a super user. */ public class OAuthSessionAuthorizer implements Authorizer { static final Logger log = LoggerFactory.getLogger(OAuthSessionAuthorizer.class); static final Logger GRANT_LOG = LoggerFactory.getLogger(OAuthSessionAuthorizer.class.getName() + ".grant"); static final Logger DENY_LOG = LoggerFactory.getLogger(OAuthSessionAuthorizer.class.getName() + ".deny"); private Authorizer delegate; @Override public void configure(java.util.Map configs) { String className = (String) configs.get(ServerConfig.STRIMZI_AUTHORIZER_DELEGATE_CLASS_NAME); if (className != null) { try { Class delegateClass = Thread.currentThread().getContextClassLoader().loadClass(className); if (!Authorizer.class.isAssignableFrom(delegateClass)) { throw new IllegalArgumentException("The class specified by " + ServerConfig.STRIMZI_AUTHORIZER_DELEGATE_CLASS_NAME + " is not an instance of org.apache.kafka.server.authorizer.Authorizer"); } delegate = (Authorizer) delegateClass.getConstructor().newInstance(); // Configure the delegate delegate.configure(configs); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw new ConfigException("Failed to instantiate and configure the delegate authorizer: " + className, e); } } else { String grantByDefault = (String) configs.get(ServerConfig.STRIMZI_AUTHORIZER_GRANT_WHEN_NO_DELEGATE); boolean isGrantByDefault = grantByDefault != null && Config.isTrue(grantByDefault); if (!isGrantByDefault) { throw new ConfigException("When no 'strimzi.authorizer.delegate.class.name' is specified, 'strimzi.authorizer.grant.when.no.delegate=true' has to be specified"); } } if (log.isDebugEnabled()) { log.debug("Configured OAuthSessionAuthorizer:\n\t{}: {}", ServerConfig.STRIMZI_AUTHORIZER_DELEGATE_CLASS_NAME, className); } } @Override public List authorize(AuthorizableRequestContext requestContext, List actions) { KafkaPrincipal principal = requestContext.principal(); if (!(principal instanceof OAuthKafkaPrincipal)) { // If user wasn't authenticated over OAuth, there's nothing for us to check if (delegate != null) { return delegate.authorize(requestContext, actions); } else { if (GRANT_LOG.isDebugEnabled()) { GRANT_LOG.debug("Authorization GRANTED - no access token: " + principal + ", actions: " + actions); } return Collections.nCopies(actions.size(), AuthorizationResult.ALLOWED); } } BearerTokenWithPayload token = ((OAuthKafkaPrincipal) principal).getJwt(); if (denyIfTokenInvalid(token)) { return Collections.nCopies(actions.size(), AuthorizationResult.DENIED); } if (delegate == null) { if (GRANT_LOG.isDebugEnabled()) { GRANT_LOG.debug("Authorization GRANTED - access token still valid: " + principal + ", actions: " + actions + ", token: " + mask(token.value())); } return Collections.nCopies(actions.size(), AuthorizationResult.ALLOWED); } return delegate.authorize(requestContext, actions); } private boolean denyIfTokenInvalid(BearerTokenWithPayload token) { if (token.lifetimeMs() <= System.currentTimeMillis()) { if (DENY_LOG.isDebugEnabled()) { DENY_LOG.debug("Authorization DENIED due to token expiry - The token expired at: " + token.lifetimeMs() + " (" + TimeUtil.formatIsoDateTimeUTC(token.lifetimeMs()) + " UTC), for token: " + mask(token.value())); } return true; } return false; } @Override public void close() throws IOException { delegate.close(); } @Override public java.util.Map> start(AuthorizerServerInfo serverInfo) { return delegate.start(serverInfo); } @Override public List> createAcls(AuthorizableRequestContext requestContext, List aclBindings) { return delegate.createAcls(requestContext, aclBindings); } @Override public List> deleteAcls(AuthorizableRequestContext requestContext, List aclBindingFilters) { return delegate.deleteAcls(requestContext, aclBindingFilters); } @Override public Iterable acls(AclBindingFilter filter) { return delegate.acls(filter); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy