On Tue, Jul 29, 2025 at 05:22:38PM +0200, Jean-Baptiste Onofré wrote:
> Hi everyone,
> 
> I submit the Apache Karaf 4.4.8 release to your vote.
> 
> This release is a maintenance release on the 4.4.x series bringing
> fixes and updates, especially:
> - SecurityContext returns RolePrincipal instead of UserPrincipal in JAX-RS
> - Several improvements on JAAS layer
> - New BoM (light)
> - Java26 support
> - Full build using JDK11 (including javase 11 in the resolver)
> - A bunch of dependency updates
> 
> You can take a look on the Release Notes for details:
> https://issues.apache.org/jira/secure/ReleaseNote.jspa?projectId=12311140&version=12354828
> 
> Maven Staging Repository:
> https://repository.apache.org/content/repositories/orgapachekaraf-1195/
> 
> Dist Staging Repository:
> https://dist.apache.org/repos/dist/dev/karaf/4.4.8/
> 
> Git tag:
> karaf-4.4.8
> 
> Please vote to approve this release:
> [ ] +1 Approve the release
> [ ] -1 Don't approve the release (please provide specific comments)
> 
> This vote will be open for at least 72 hours.
> 
> Regards
> JB

Hello,

Currently I use the karaf service guard with an acl for my service. This
works for the shell, as well as my JAX-RS setup (allowing both a REST
and CLI based authc/authz with a centralized policy). While I use Shiro
on the REST side, I wrap the invocations with an interceptor to populate
what Karaf wants for doing lookups.

@Slf4j
public class JaasSubjectDoAsInterceptor extends 
AbstractPhaseInterceptor<Message> {

  public JaasSubjectDoAsInterceptor() {
    super(Phase.PRE_INVOKE);
  }

  @Override
  public void handleMessage(final Message message) {
    final org.apache.shiro.subject.Subject shiroSubject = 
SecurityUtils.getSubject();
    if (shiroSubject == null
        || !(shiroSubject.getPrincipal() instanceof RichPrincipal 
userPrincipal)) {
      return;
    }
    SecurityUtilities.runAs(
        SecurityUtilities.toJaasSubject(userPrincipal),
        () -> {
          message.getInterceptorChain().doIntercept(message);
          return null;
        });
  }
}

where SecurityUtilities.runAs:

  public static <T> T runAs(final Subject subject, final PrivilegedAction<T> 
action) {
    if (CALL_AS_METHOD.isPresent()) {
      try {
        // Java 21+ path: invoke Subject.callAs(subject, action)
        @SuppressWarnings("unchecked")
        final T result = (T) CALL_AS_METHOD.get().invoke(null, subject, action);
        return result;
      } catch (final ReflectiveOperationException ex) {
        // Fallback to legacy doAs on error
      }
    }

    // Pre-Java 21 fallback or callAs failed: use legacy Subject.doAs(...)
    return Subject.doAs(subject, action);
  }

I also have SecurityUtilities.toJaasSubject:

  public static Subject toJaasSubject(@NonNull final RichPrincipal 
richPrincipal) {
    final Set<Principal> principals = new HashSet<>();

    // Add the main user principal (identity) to the JAAS Subject
    principals.add(new UserPrincipal(richPrincipal.name()));

    // Add roles as RolePrincipals
    richPrincipal.roles().forEach(role -> principals.add(new 
RolePrincipal(role)));

    // Add permissions as RolePrincipals (assuming permissions are handled 
similarly)
    richPrincipal
        .permissions()
        .forEach(permission -> principals.add(new RolePrincipal(permission)));

    // Construct the JAAS Subject with principals, no public/private credentials
    return new Subject(true, principals, Set.of(), Set.of());
  }

This allows me to authenticate to the REST service and still use the
service guard to check for my authorization (roles) because of populated
the JAASSubject with roles from User/Roles Karaf expects.

I do something similar when accessing the Subject from the service as
in:

  public static Optional<Subject> getCurrentSubject() {
    if (CURRENT_METHOD.isPresent()) {
      try {
        // Java 21+ path: invoke Subject.current()
        final Subject subject = (Subject) CURRENT_METHOD.get().invoke(null);
        return Optional.ofNullable(subject);
      } catch (final ReflectiveOperationException e) {
        // Fallback to empty on error
        return Optional.empty();
      }
    } else {
      // Pre-Java 21 fallback: use legacy method
      final Subject subject = Subject.getSubject(AccessController.getContext());
      return Optional.ofNullable(subject);
    }
  }

Another future proof using Subject.current().

Question:

Are the changes proposed here going to break all this for me?

Reply via email to