You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@karaf.apache.org by Matthias Leinweber <m....@datatactics.de> on 2022/06/22 19:53:39 UTC

aries-jax-rs-whiteboard

Hello Karaf user,

i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running without
success. 2.0.1 with installed cxf is working fine. But in 1.0.7-1.0.10.
http:list does not show a servlet and my jaxrs whiteboard resources are not
working.

Any hints which component needs to be installed? Reading BNDrun +
Components DSL is pretty confusing.

Btw, does anyone have a good example for authentication? Shiro seems to be
a bit overkill.

br,
Matthias

Re: aries-jax-rs-whiteboard

Posted by Matthias Leinweber <m....@datatactics.de>.
Thank you very much Joao...
lets see if i can somehow connect this with karaf jaas service

br,
Matthias

Am Mo., 27. Juni 2022 um 17:40 Uhr schrieb João Assunção <
joao.assuncao@exploitsys.com>:

> Hello Matthias.
>
> Sorry but I don't have a public repo with an example.
> I had some issues in the past when using more recent versions of Aries JAX
> RS due to conflicts with the CXF version I was using in another service, so
> I kept using version 1.0.4.
> In my latest projects, where I needed more customization, I ditched Aries
> JAX RS and used CXF directly. Needed more code and had to use
> JAXRSServerFactoryBean to create the server. Also lost the possibility to
> use service injection :-(
>
> The code for JWT is mostly taken from jose4j examples:
>
> @Component(service = JwtService.class, //
>>         immediate = true, //
>>         scope = ServiceScope.SINGLETON)
>> public class JwtService {
>>
>>     private static final Logger LOGGER =
>> LoggerFactory.getLogger(JwtService.class);
>>
>>     // time when the token will expire (minutes)
>>     private static final float TOKEN_TTL = 30;
>>
>>     public static final String ISSUER = "ACME";
>>
>>     public static final String AUDIENCE = "universe";
>>
>>     public static final String CLAIM_NAME = "name";
>>
>>     public static final String CLAIM_PERMISSIONS = "permissions";
>>
>>     private RsaJsonWebKey rsaJsonWebKey;
>>
>>     private JwtConsumer jwtConsumer;
>>
>>     @Activate
>>     public void activate() throws JoseException {
>>
>>         initWebKey();
>>         initJWTconsumer();
>>     }
>>
>>     private void initWebKey() throws JoseException {
>>
>>         rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
>>         rsaJsonWebKey.setKeyId("keyId");
>>
>>     }
>>
>>     private void initJWTconsumer() {
>>
>>         jwtConsumer = new JwtConsumerBuilder()
>>                 .setRequireExpirationTime() // the JWT must have an
>> expiration
>>                                             // time
>>                 .setAllowedClockSkewInSeconds(30) // allow some leeway in
>>                                                   // validating time based
>>                                                   // claims to account
>> for clock
>>                                                   // skew
>>                 .setRequireSubject() // the JWT must have a subject claim
>>                 .setExpectedIssuer(ISSUER) // whom the JWT needs to have
>> been
>>                                            // issued by
>>                 .setExpectedAudience(AUDIENCE) // to whom the JWT is
>> intended
>>                                                // for
>>                 .setVerificationKey(rsaJsonWebKey.getKey()) // verify the
>>                                                             // signature
>> with
>>                                                             // the public
>> key
>>                 .setJwsAlgorithmConstraints( // only allow the expected
>>                                              // signature algorithm(s) in
>> the
>>                                              // given context
>>                         new
>> AlgorithmConstraints(ConstraintType.WHITELIST, // which
>>
>>  // is
>>
>>  // only
>>
>>  // RS256
>>
>>  // here
>>                                 AlgorithmIdentifiers.RSA_USING_SHA256))
>>                 .build(); // create the JwtConsumer instance
>>
>>     }
>>
>>     public String createToken(SubjectDetails subjectDetails) throws
>> AuthenticationException {
>>
>>         // Create the Claims, which will be the content of the JWT
>>         JwtClaims claims = new JwtClaims();
>>         claims.setIssuer(ISSUER); // who creates the token and signs it
>>         claims.setAudience(AUDIENCE); // to whom the token is intended to
>> be
>>                                       // sent
>>         claims.setExpirationTimeMinutesInTheFuture(TOKEN_TTL);
>>         claims.setGeneratedJwtId(); // a unique identifier for the token
>>         claims.setIssuedAtToNow(); // when the token was issued/created
>> (now)
>>         claims.setNotBeforeMinutesInThePast(2);
>>         claims.setSubject(subjectDetails.getId());
>>         claims.setStringClaim(CLAIM_NAME, subjectDetails.getName());
>>         claims.setStringListClaim(CLAIM_PERMISSIONS,
>> subjectDetails.getPermissionsList());
>>
>>         // A JWT is a JWS and/or a JWE with JSON claims as the payload.
>>         // In this example it is a JWS so we create a JsonWebSignature
>> object.
>>         JsonWebSignature jws = new JsonWebSignature();
>>
>>         // The payload of the JWS is JSON content of the JWT Claims
>>         jws.setPayload(claims.toJson());
>>
>>         // The JWT is signed using the private key
>>         jws.setKey(rsaJsonWebKey.getPrivateKey());
>>
>>         jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
>>
>>         // Set the signature algorithm on the JWT/JWS that will integrity
>>         // protect the claims
>>
>> jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
>>
>>         try {
>>             return jws.getCompactSerialization();
>>         }
>>         catch (JoseException ex) {
>>             throw new AuthenticationException("Token creation failed",
>> ex);
>>         }
>>     }
>>
>>     public SubjectDetails validateToken(String token) throws
>> AuthenticationException {
>>
>>         try {
>>             JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
>>             String subjectName =
>> jwtClaims.getStringClaimValue(CLAIM_NAME);
>>             List<String> permissions =
>> jwtClaims.getStringListClaimValue(CLAIM_PERMISSIONS);
>>             Set<Permission> permissionsSet =
>> Permission.fromList(permissions);
>>             permissionsSet.add(Permission.AUTHENTICATED); // Implicit
>> permission
>>             return new SubjectDetails(jwtClaims.getSubject(),
>> subjectName, permissionsSet);
>>         }
>>         catch (InvalidJwtException ex) {
>>             logException("InvalidJwtException", ex);
>>             throw new AuthenticationException(ex.getMessage());
>>
>>         }
>>         catch (MalformedClaimException ex) {
>>             logException("MalformedClaimException", ex);
>>             throw new AuthenticationException(ex.getMessage());
>>         }
>>
>>     }
>>
>>     protected static void logException(String msg, Exception ex) {
>>
>>         LOGGER.debug(msg, ex);
>>     }
>> }
>>
>
>
>
> João Assunção
>
> Email: joao.assuncao@exploitsys.com
> Mobile: +351 916968984
> Phone: +351 211933149
> Web: www.exploitsys.com
>
>
>
>
> On Mon, Jun 27, 2022 at 9:33 AM Matthias Leinweber <
> m.leinweber@datatactics.de> wrote:
>
>> Hello,
>>
>> I am using karaf 4.3.7 and yes I saw the examples in karaf. The version
>> used is 1.0.6 which is pretty old. I tried to update to 1.0.10 but i dont
>> see what is going wrong. There is the Application, Servlet, and Whiteboard.
>> But the servlet is not accessible.
>> With version 2.0.1 is cxf-core required. That would also be fine.
>> Unfortunately it does not work without a url prefix which would require a
>> change on the client side.
>>
>> @ João thank you very much for your help. Do you also have a repo link to
>> a working example; where does the JWTService come from?
>>
>> Thank
>> br,
>> Matthias
>>
>> Am Do., 23. Juni 2022 um 11:02 Uhr schrieb João Assunção <
>> joao.assuncao@exploitsys.com>:
>>
>>> Hello Matthias,
>>>
>>> Regarding authentication I normally use a ContainerRequestFilter.
>>> Follows an example of a filter where authentication is done using a JWT
>>> token.
>>> It uses JwtService that is responsible for creating and validating
>>> tokens. It uses jose4j.
>>>
>>>
>>> @Secured
>>>> @Provider
>>>> @Priority(Priorities.AUTHENTICATION)
>>>> @Component(scope = ServiceScope.PROTOTYPE, //
>>>>         service = { ContainerRequestFilter.class }, //
>>>>         property = {
>>>>                 JaxrsWhiteboardConstants.JAX_RS_EXTENSION + "=" + true,
>>>> //
>>>>                 JaxrsWhiteboardConstants.JAX_RS_NAME + "=" +
>>>> "TokenAuthenticationFilter", //
>>>>                 Constants.JAX_RS_APPLICATION_SELECT, //
>>>>         })
>>>> public class TokenAuthenticationFilter implements
>>>> ContainerRequestFilter {
>>>>
>>>>     private static final Logger LOGGER =
>>>> LoggerFactory.getLogger(TokenAuthenticationFilter.class);
>>>>
>>>>     private static final String AUTHENTICATION_SCHEME = "Bearer";
>>>>
>>>>     private static final String AUTHENTICATION_SCHEME_PREFIX =
>>>> AUTHENTICATION_SCHEME + " ";
>>>>
>>>>     @Reference(cardinality = ReferenceCardinality.MANDATORY)
>>>>     JwtService jwtService;
>>>>
>>>>     @Context
>>>>     private ResourceInfo resourceInfo;
>>>>
>>>>     @Override
>>>>     public void filter(ContainerRequestContext requestContext) throws
>>>> IOException {
>>>>
>>>>         // Get the Authorization header from the request
>>>>         String authorizationHeader =
>>>> requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
>>>>         LOGGER.debug("authorizationHeader {}", authorizationHeader);
>>>>         // Validate the Authorization header
>>>>         if (!isTokenBasedAuthentication(authorizationHeader)) {
>>>>             LOGGER.info("Missing auth token. Aborting");
>>>>             abortWithUnauthorized(requestContext);
>>>>             return;
>>>>         }
>>>>         // Extract the token from the Authorization header
>>>>         final String token =
>>>> authorizationHeader.substring(AUTHENTICATION_SCHEME_PREFIX.length());
>>>>         try {
>>>>             // Validate the token. An exception will be thrown if
>>>> invalid
>>>>             SubjectDetails subjectDetails =
>>>> jwtService.validateToken(token);
>>>>             LOGGER.debug("Token for subject {} accepted",
>>>> subjectDetails.getId());
>>>>             initSecurityContext(requestContext, subjectDetails);
>>>>             Permission requiredPermission =
>>>> getPermissionForResource(resourceInfo);
>>>>             if (!subjectDetails.hasPermission(requiredPermission)) {
>>>>                 LOGGER.debug("subject {} lack permission {}",
>>>> subjectDetails.getId(), requiredPermission);
>>>>                 abortWithForbidden(requestContext);
>>>>             }
>>>>         }
>>>>         catch (AuthenticationException e) {
>>>>             LOGGER.trace("Ignored", e);
>>>>             LOGGER.info("Token validation failed. Aborting");
>>>>             abortWithUnauthorized(requestContext);
>>>>         }
>>>>     }
>>>>
>>>>     /**
>>>>      * @param requestContext
>>>>      * @param subjectDetails
>>>>      */
>>>>     private void initSecurityContext(ContainerRequestContext
>>>> requestContext, SubjectDetails subjectDetails) {
>>>>
>>>>         final SecurityContext currentSecurityContext =
>>>> requestContext.getSecurityContext();
>>>>         boolean secure = currentSecurityContext.isSecure();
>>>>         requestContext.setSecurityContext(new
>>>> InternalSecurityContext(subjectDetails, secure));
>>>>
>>>>     }
>>>>
>>>>     private boolean isTokenBasedAuthentication(String
>>>> authorizationHeader) {
>>>>
>>>>         // Check if the Authorization header is valid
>>>>         // It must not be null and must be prefixed with "Bearer" plus a
>>>>         // whitespace
>>>>         // The authentication scheme comparison must be case-insensitive
>>>>         return StringUtils.startsWithIgnoreCase(authorizationHeader,
>>>> AUTHENTICATION_SCHEME_PREFIX);
>>>>     }
>>>>
>>>>     private void abortWithUnauthorized(ContainerRequestContext
>>>> requestContext) {
>>>>
>>>>         // Abort the filter chain with a 401 status code response
>>>>         // The WWW-Authenticate header is sent along with the response
>>>>         requestContext.abortWith(
>>>>                 Response.status(Response.Status.UNAUTHORIZED)
>>>>                         .header(HttpHeaders.WWW_AUTHENTICATE,
>>>>                                 AUTHENTICATION_SCHEME + " realm=\"" +
>>>> Constants.APP_NAME + "\"")
>>>>                         .build());
>>>>     }
>>>>
>>>>     private void abortWithForbidden(ContainerRequestContext
>>>> requestContext) {
>>>>
>>>>
>>>> requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
>>>>     }
>>>>
>>>>     static Permission getPermissionForResource(ResourceInfo
>>>> resourceInfo) {
>>>>
>>>>         Permission permission =
>>>> extractPermission(resourceInfo.getResourceMethod());
>>>>         if (permission != null) {
>>>>             return permission;
>>>>         }
>>>>         permission = extractPermission(resourceInfo.getResourceClass());
>>>>         return permission;
>>>>
>>>>     }
>>>>
>>>>     // Extract Permission from the annotated element
>>>>     static Permission extractPermission(AnnotatedElement
>>>> annotatedElement) {
>>>>
>>>>         if (annotatedElement == null) {
>>>>             return null;
>>>>         }
>>>>         Secured secured = annotatedElement.getAnnotation(Secured.class);
>>>>         return secured == null ? null : secured.value();
>>>>     }
>>>>
>>>>     private static class InternalSecurityContext implements
>>>> SecurityContext {
>>>>
>>>>         private final boolean secure;
>>>>
>>>>         private final SubjectDetails subjectDetails;
>>>>
>>>>         InternalSecurityContext(SubjectDetails subjectDetails, boolean
>>>> secure) {
>>>>
>>>>             this.subjectDetails = subjectDetails;
>>>>             this.secure = secure;
>>>>         }
>>>>
>>>>         @Override
>>>>         public Principal getUserPrincipal() {
>>>>
>>>>             return subjectDetails;
>>>>         }
>>>>
>>>>         @Override
>>>>         public boolean isUserInRole(String role) {
>>>>
>>>>             // We are not using role base authorization
>>>>             return true;
>>>>         }
>>>>
>>>>         @Override
>>>>         public boolean isSecure() {
>>>>
>>>>             return secure;
>>>>         }
>>>>
>>>>         @Override
>>>>         public String getAuthenticationScheme() {
>>>>
>>>>             return AUTHENTICATION_SCHEME;
>>>>         }
>>>>
>>>>     }
>>>>
>>>>     /**
>>>>      * For test purposes
>>>>      *
>>>>      * @param jwtService
>>>>      * @return
>>>>      */
>>>>     public static TokenAuthenticationFilter
>>>> createTestInstance(JwtService jwtService) {
>>>>
>>>>         TokenAuthenticationFilter filter = new
>>>> TokenAuthenticationFilter();
>>>>         filter.jwtService = jwtService;
>>>>         return filter;
>>>>     }
>>>> }
>>>>
>>>
>>> The Secured annotation is used to mark the methods that need to be
>>> protected and the required permission.
>>>
>>> @NameBinding
>>>> @Retention(RetentionPolicy.RUNTIME)
>>>> @Target({ ElementType.TYPE, ElementType.METHOD })
>>>> public @interface Secured {
>>>>
>>>>     Permission value() default Permission.AUTHENTICATED;
>>>> }
>>>>
>>>
>>> João Assunção
>>>
>>> Email: joao.assuncao@exploitsys.com
>>> Mobile: +351 916968984
>>> Phone: +351 211933149
>>> Web: www.exploitsys.com
>>>
>>>
>>>
>>>
>>> On Wed, Jun 22, 2022 at 8:54 PM Matthias Leinweber <
>>> m.leinweber@datatactics.de> wrote:
>>>
>>>> Hello Karaf user,
>>>>
>>>> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running
>>>> without success. 2.0.1 with installed cxf is working fine. But in
>>>> 1.0.7-1.0.10. http:list does not show a servlet and my jaxrs whiteboard
>>>> resources are not working.
>>>>
>>>> Any hints which component needs to be installed? Reading BNDrun +
>>>> Components DSL is pretty confusing.
>>>>
>>>> Btw, does anyone have a good example for authentication? Shiro seems to
>>>> be a bit overkill.
>>>>
>>>> br,
>>>> Matthias
>>>>
>>>
>>
>>
>>

Re: aries-jax-rs-whiteboard

Posted by João Assunção <jo...@exploitsys.com>.
Hello Matthias.

Sorry but I don't have a public repo with an example.
I had some issues in the past when using more recent versions of Aries JAX
RS due to conflicts with the CXF version I was using in another service, so
I kept using version 1.0.4.
In my latest projects, where I needed more customization, I ditched Aries
JAX RS and used CXF directly. Needed more code and had to use
JAXRSServerFactoryBean to create the server. Also lost the possibility to
use service injection :-(

The code for JWT is mostly taken from jose4j examples:

@Component(service = JwtService.class, //
>         immediate = true, //
>         scope = ServiceScope.SINGLETON)
> public class JwtService {
>
>     private static final Logger LOGGER =
> LoggerFactory.getLogger(JwtService.class);
>
>     // time when the token will expire (minutes)
>     private static final float TOKEN_TTL = 30;
>
>     public static final String ISSUER = "ACME";
>
>     public static final String AUDIENCE = "universe";
>
>     public static final String CLAIM_NAME = "name";
>
>     public static final String CLAIM_PERMISSIONS = "permissions";
>
>     private RsaJsonWebKey rsaJsonWebKey;
>
>     private JwtConsumer jwtConsumer;
>
>     @Activate
>     public void activate() throws JoseException {
>
>         initWebKey();
>         initJWTconsumer();
>     }
>
>     private void initWebKey() throws JoseException {
>
>         rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
>         rsaJsonWebKey.setKeyId("keyId");
>
>     }
>
>     private void initJWTconsumer() {
>
>         jwtConsumer = new JwtConsumerBuilder()
>                 .setRequireExpirationTime() // the JWT must have an
> expiration
>                                             // time
>                 .setAllowedClockSkewInSeconds(30) // allow some leeway in
>                                                   // validating time based
>                                                   // claims to account for
> clock
>                                                   // skew
>                 .setRequireSubject() // the JWT must have a subject claim
>                 .setExpectedIssuer(ISSUER) // whom the JWT needs to have
> been
>                                            // issued by
>                 .setExpectedAudience(AUDIENCE) // to whom the JWT is
> intended
>                                                // for
>                 .setVerificationKey(rsaJsonWebKey.getKey()) // verify the
>                                                             // signature
> with
>                                                             // the public
> key
>                 .setJwsAlgorithmConstraints( // only allow the expected
>                                              // signature algorithm(s) in
> the
>                                              // given context
>                         new AlgorithmConstraints(ConstraintType.WHITELIST,
> // which
>
>  // is
>
>  // only
>
>  // RS256
>
>  // here
>                                 AlgorithmIdentifiers.RSA_USING_SHA256))
>                 .build(); // create the JwtConsumer instance
>
>     }
>
>     public String createToken(SubjectDetails subjectDetails) throws
> AuthenticationException {
>
>         // Create the Claims, which will be the content of the JWT
>         JwtClaims claims = new JwtClaims();
>         claims.setIssuer(ISSUER); // who creates the token and signs it
>         claims.setAudience(AUDIENCE); // to whom the token is intended to
> be
>                                       // sent
>         claims.setExpirationTimeMinutesInTheFuture(TOKEN_TTL);
>         claims.setGeneratedJwtId(); // a unique identifier for the token
>         claims.setIssuedAtToNow(); // when the token was issued/created
> (now)
>         claims.setNotBeforeMinutesInThePast(2);
>         claims.setSubject(subjectDetails.getId());
>         claims.setStringClaim(CLAIM_NAME, subjectDetails.getName());
>         claims.setStringListClaim(CLAIM_PERMISSIONS,
> subjectDetails.getPermissionsList());
>
>         // A JWT is a JWS and/or a JWE with JSON claims as the payload.
>         // In this example it is a JWS so we create a JsonWebSignature
> object.
>         JsonWebSignature jws = new JsonWebSignature();
>
>         // The payload of the JWS is JSON content of the JWT Claims
>         jws.setPayload(claims.toJson());
>
>         // The JWT is signed using the private key
>         jws.setKey(rsaJsonWebKey.getPrivateKey());
>
>         jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId());
>
>         // Set the signature algorithm on the JWT/JWS that will integrity
>         // protect the claims
>         jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
>
>         try {
>             return jws.getCompactSerialization();
>         }
>         catch (JoseException ex) {
>             throw new AuthenticationException("Token creation failed", ex);
>         }
>     }
>
>     public SubjectDetails validateToken(String token) throws
> AuthenticationException {
>
>         try {
>             JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
>             String subjectName = jwtClaims.getStringClaimValue(CLAIM_NAME);
>             List<String> permissions =
> jwtClaims.getStringListClaimValue(CLAIM_PERMISSIONS);
>             Set<Permission> permissionsSet =
> Permission.fromList(permissions);
>             permissionsSet.add(Permission.AUTHENTICATED); // Implicit
> permission
>             return new SubjectDetails(jwtClaims.getSubject(), subjectName,
> permissionsSet);
>         }
>         catch (InvalidJwtException ex) {
>             logException("InvalidJwtException", ex);
>             throw new AuthenticationException(ex.getMessage());
>
>         }
>         catch (MalformedClaimException ex) {
>             logException("MalformedClaimException", ex);
>             throw new AuthenticationException(ex.getMessage());
>         }
>
>     }
>
>     protected static void logException(String msg, Exception ex) {
>
>         LOGGER.debug(msg, ex);
>     }
> }
>



João Assunção

Email: joao.assuncao@exploitsys.com
Mobile: +351 916968984
Phone: +351 211933149
Web: www.exploitsys.com




On Mon, Jun 27, 2022 at 9:33 AM Matthias Leinweber <
m.leinweber@datatactics.de> wrote:

> Hello,
>
> I am using karaf 4.3.7 and yes I saw the examples in karaf. The version
> used is 1.0.6 which is pretty old. I tried to update to 1.0.10 but i dont
> see what is going wrong. There is the Application, Servlet, and Whiteboard.
> But the servlet is not accessible.
> With version 2.0.1 is cxf-core required. That would also be fine.
> Unfortunately it does not work without a url prefix which would require a
> change on the client side.
>
> @ João thank you very much for your help. Do you also have a repo link to
> a working example; where does the JWTService come from?
>
> Thank
> br,
> Matthias
>
> Am Do., 23. Juni 2022 um 11:02 Uhr schrieb João Assunção <
> joao.assuncao@exploitsys.com>:
>
>> Hello Matthias,
>>
>> Regarding authentication I normally use a ContainerRequestFilter.
>> Follows an example of a filter where authentication is done using a JWT
>> token.
>> It uses JwtService that is responsible for creating and validating
>> tokens. It uses jose4j.
>>
>>
>> @Secured
>>> @Provider
>>> @Priority(Priorities.AUTHENTICATION)
>>> @Component(scope = ServiceScope.PROTOTYPE, //
>>>         service = { ContainerRequestFilter.class }, //
>>>         property = {
>>>                 JaxrsWhiteboardConstants.JAX_RS_EXTENSION + "=" + true,
>>> //
>>>                 JaxrsWhiteboardConstants.JAX_RS_NAME + "=" +
>>> "TokenAuthenticationFilter", //
>>>                 Constants.JAX_RS_APPLICATION_SELECT, //
>>>         })
>>> public class TokenAuthenticationFilter implements ContainerRequestFilter
>>> {
>>>
>>>     private static final Logger LOGGER =
>>> LoggerFactory.getLogger(TokenAuthenticationFilter.class);
>>>
>>>     private static final String AUTHENTICATION_SCHEME = "Bearer";
>>>
>>>     private static final String AUTHENTICATION_SCHEME_PREFIX =
>>> AUTHENTICATION_SCHEME + " ";
>>>
>>>     @Reference(cardinality = ReferenceCardinality.MANDATORY)
>>>     JwtService jwtService;
>>>
>>>     @Context
>>>     private ResourceInfo resourceInfo;
>>>
>>>     @Override
>>>     public void filter(ContainerRequestContext requestContext) throws
>>> IOException {
>>>
>>>         // Get the Authorization header from the request
>>>         String authorizationHeader =
>>> requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
>>>         LOGGER.debug("authorizationHeader {}", authorizationHeader);
>>>         // Validate the Authorization header
>>>         if (!isTokenBasedAuthentication(authorizationHeader)) {
>>>             LOGGER.info("Missing auth token. Aborting");
>>>             abortWithUnauthorized(requestContext);
>>>             return;
>>>         }
>>>         // Extract the token from the Authorization header
>>>         final String token =
>>> authorizationHeader.substring(AUTHENTICATION_SCHEME_PREFIX.length());
>>>         try {
>>>             // Validate the token. An exception will be thrown if invalid
>>>             SubjectDetails subjectDetails =
>>> jwtService.validateToken(token);
>>>             LOGGER.debug("Token for subject {} accepted",
>>> subjectDetails.getId());
>>>             initSecurityContext(requestContext, subjectDetails);
>>>             Permission requiredPermission =
>>> getPermissionForResource(resourceInfo);
>>>             if (!subjectDetails.hasPermission(requiredPermission)) {
>>>                 LOGGER.debug("subject {} lack permission {}",
>>> subjectDetails.getId(), requiredPermission);
>>>                 abortWithForbidden(requestContext);
>>>             }
>>>         }
>>>         catch (AuthenticationException e) {
>>>             LOGGER.trace("Ignored", e);
>>>             LOGGER.info("Token validation failed. Aborting");
>>>             abortWithUnauthorized(requestContext);
>>>         }
>>>     }
>>>
>>>     /**
>>>      * @param requestContext
>>>      * @param subjectDetails
>>>      */
>>>     private void initSecurityContext(ContainerRequestContext
>>> requestContext, SubjectDetails subjectDetails) {
>>>
>>>         final SecurityContext currentSecurityContext =
>>> requestContext.getSecurityContext();
>>>         boolean secure = currentSecurityContext.isSecure();
>>>         requestContext.setSecurityContext(new
>>> InternalSecurityContext(subjectDetails, secure));
>>>
>>>     }
>>>
>>>     private boolean isTokenBasedAuthentication(String
>>> authorizationHeader) {
>>>
>>>         // Check if the Authorization header is valid
>>>         // It must not be null and must be prefixed with "Bearer" plus a
>>>         // whitespace
>>>         // The authentication scheme comparison must be case-insensitive
>>>         return StringUtils.startsWithIgnoreCase(authorizationHeader,
>>> AUTHENTICATION_SCHEME_PREFIX);
>>>     }
>>>
>>>     private void abortWithUnauthorized(ContainerRequestContext
>>> requestContext) {
>>>
>>>         // Abort the filter chain with a 401 status code response
>>>         // The WWW-Authenticate header is sent along with the response
>>>         requestContext.abortWith(
>>>                 Response.status(Response.Status.UNAUTHORIZED)
>>>                         .header(HttpHeaders.WWW_AUTHENTICATE,
>>>                                 AUTHENTICATION_SCHEME + " realm=\"" +
>>> Constants.APP_NAME + "\"")
>>>                         .build());
>>>     }
>>>
>>>     private void abortWithForbidden(ContainerRequestContext
>>> requestContext) {
>>>
>>>
>>> requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
>>>     }
>>>
>>>     static Permission getPermissionForResource(ResourceInfo
>>> resourceInfo) {
>>>
>>>         Permission permission =
>>> extractPermission(resourceInfo.getResourceMethod());
>>>         if (permission != null) {
>>>             return permission;
>>>         }
>>>         permission = extractPermission(resourceInfo.getResourceClass());
>>>         return permission;
>>>
>>>     }
>>>
>>>     // Extract Permission from the annotated element
>>>     static Permission extractPermission(AnnotatedElement
>>> annotatedElement) {
>>>
>>>         if (annotatedElement == null) {
>>>             return null;
>>>         }
>>>         Secured secured = annotatedElement.getAnnotation(Secured.class);
>>>         return secured == null ? null : secured.value();
>>>     }
>>>
>>>     private static class InternalSecurityContext implements
>>> SecurityContext {
>>>
>>>         private final boolean secure;
>>>
>>>         private final SubjectDetails subjectDetails;
>>>
>>>         InternalSecurityContext(SubjectDetails subjectDetails, boolean
>>> secure) {
>>>
>>>             this.subjectDetails = subjectDetails;
>>>             this.secure = secure;
>>>         }
>>>
>>>         @Override
>>>         public Principal getUserPrincipal() {
>>>
>>>             return subjectDetails;
>>>         }
>>>
>>>         @Override
>>>         public boolean isUserInRole(String role) {
>>>
>>>             // We are not using role base authorization
>>>             return true;
>>>         }
>>>
>>>         @Override
>>>         public boolean isSecure() {
>>>
>>>             return secure;
>>>         }
>>>
>>>         @Override
>>>         public String getAuthenticationScheme() {
>>>
>>>             return AUTHENTICATION_SCHEME;
>>>         }
>>>
>>>     }
>>>
>>>     /**
>>>      * For test purposes
>>>      *
>>>      * @param jwtService
>>>      * @return
>>>      */
>>>     public static TokenAuthenticationFilter
>>> createTestInstance(JwtService jwtService) {
>>>
>>>         TokenAuthenticationFilter filter = new
>>> TokenAuthenticationFilter();
>>>         filter.jwtService = jwtService;
>>>         return filter;
>>>     }
>>> }
>>>
>>
>> The Secured annotation is used to mark the methods that need to be
>> protected and the required permission.
>>
>> @NameBinding
>>> @Retention(RetentionPolicy.RUNTIME)
>>> @Target({ ElementType.TYPE, ElementType.METHOD })
>>> public @interface Secured {
>>>
>>>     Permission value() default Permission.AUTHENTICATED;
>>> }
>>>
>>
>> João Assunção
>>
>> Email: joao.assuncao@exploitsys.com
>> Mobile: +351 916968984
>> Phone: +351 211933149
>> Web: www.exploitsys.com
>>
>>
>>
>>
>> On Wed, Jun 22, 2022 at 8:54 PM Matthias Leinweber <
>> m.leinweber@datatactics.de> wrote:
>>
>>> Hello Karaf user,
>>>
>>> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running
>>> without success. 2.0.1 with installed cxf is working fine. But in
>>> 1.0.7-1.0.10. http:list does not show a servlet and my jaxrs whiteboard
>>> resources are not working.
>>>
>>> Any hints which component needs to be installed? Reading BNDrun +
>>> Components DSL is pretty confusing.
>>>
>>> Btw, does anyone have a good example for authentication? Shiro seems to
>>> be a bit overkill.
>>>
>>> br,
>>> Matthias
>>>
>>
>
>
>

Re: aries-jax-rs-whiteboard

Posted by Matthias Leinweber <m....@datatactics.de>.
Hello,

I am using karaf 4.3.7 and yes I saw the examples in karaf. The version
used is 1.0.6 which is pretty old. I tried to update to 1.0.10 but i dont
see what is going wrong. There is the Application, Servlet, and Whiteboard.
But the servlet is not accessible.
With version 2.0.1 is cxf-core required. That would also be fine.
Unfortunately it does not work without a url prefix which would require a
change on the client side.

@ João thank you very much for your help. Do you also have a repo link to a
working example; where does the JWTService come from?

Thank
br,
Matthias

Am Do., 23. Juni 2022 um 11:02 Uhr schrieb João Assunção <
joao.assuncao@exploitsys.com>:

> Hello Matthias,
>
> Regarding authentication I normally use a ContainerRequestFilter.
> Follows an example of a filter where authentication is done using a JWT
> token.
> It uses JwtService that is responsible for creating and validating tokens.
> It uses jose4j.
>
>
> @Secured
>> @Provider
>> @Priority(Priorities.AUTHENTICATION)
>> @Component(scope = ServiceScope.PROTOTYPE, //
>>         service = { ContainerRequestFilter.class }, //
>>         property = {
>>                 JaxrsWhiteboardConstants.JAX_RS_EXTENSION + "=" + true, //
>>                 JaxrsWhiteboardConstants.JAX_RS_NAME + "=" +
>> "TokenAuthenticationFilter", //
>>                 Constants.JAX_RS_APPLICATION_SELECT, //
>>         })
>> public class TokenAuthenticationFilter implements ContainerRequestFilter {
>>
>>     private static final Logger LOGGER =
>> LoggerFactory.getLogger(TokenAuthenticationFilter.class);
>>
>>     private static final String AUTHENTICATION_SCHEME = "Bearer";
>>
>>     private static final String AUTHENTICATION_SCHEME_PREFIX =
>> AUTHENTICATION_SCHEME + " ";
>>
>>     @Reference(cardinality = ReferenceCardinality.MANDATORY)
>>     JwtService jwtService;
>>
>>     @Context
>>     private ResourceInfo resourceInfo;
>>
>>     @Override
>>     public void filter(ContainerRequestContext requestContext) throws
>> IOException {
>>
>>         // Get the Authorization header from the request
>>         String authorizationHeader =
>> requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
>>         LOGGER.debug("authorizationHeader {}", authorizationHeader);
>>         // Validate the Authorization header
>>         if (!isTokenBasedAuthentication(authorizationHeader)) {
>>             LOGGER.info("Missing auth token. Aborting");
>>             abortWithUnauthorized(requestContext);
>>             return;
>>         }
>>         // Extract the token from the Authorization header
>>         final String token =
>> authorizationHeader.substring(AUTHENTICATION_SCHEME_PREFIX.length());
>>         try {
>>             // Validate the token. An exception will be thrown if invalid
>>             SubjectDetails subjectDetails =
>> jwtService.validateToken(token);
>>             LOGGER.debug("Token for subject {} accepted",
>> subjectDetails.getId());
>>             initSecurityContext(requestContext, subjectDetails);
>>             Permission requiredPermission =
>> getPermissionForResource(resourceInfo);
>>             if (!subjectDetails.hasPermission(requiredPermission)) {
>>                 LOGGER.debug("subject {} lack permission {}",
>> subjectDetails.getId(), requiredPermission);
>>                 abortWithForbidden(requestContext);
>>             }
>>         }
>>         catch (AuthenticationException e) {
>>             LOGGER.trace("Ignored", e);
>>             LOGGER.info("Token validation failed. Aborting");
>>             abortWithUnauthorized(requestContext);
>>         }
>>     }
>>
>>     /**
>>      * @param requestContext
>>      * @param subjectDetails
>>      */
>>     private void initSecurityContext(ContainerRequestContext
>> requestContext, SubjectDetails subjectDetails) {
>>
>>         final SecurityContext currentSecurityContext =
>> requestContext.getSecurityContext();
>>         boolean secure = currentSecurityContext.isSecure();
>>         requestContext.setSecurityContext(new
>> InternalSecurityContext(subjectDetails, secure));
>>
>>     }
>>
>>     private boolean isTokenBasedAuthentication(String
>> authorizationHeader) {
>>
>>         // Check if the Authorization header is valid
>>         // It must not be null and must be prefixed with "Bearer" plus a
>>         // whitespace
>>         // The authentication scheme comparison must be case-insensitive
>>         return StringUtils.startsWithIgnoreCase(authorizationHeader,
>> AUTHENTICATION_SCHEME_PREFIX);
>>     }
>>
>>     private void abortWithUnauthorized(ContainerRequestContext
>> requestContext) {
>>
>>         // Abort the filter chain with a 401 status code response
>>         // The WWW-Authenticate header is sent along with the response
>>         requestContext.abortWith(
>>                 Response.status(Response.Status.UNAUTHORIZED)
>>                         .header(HttpHeaders.WWW_AUTHENTICATE,
>>                                 AUTHENTICATION_SCHEME + " realm=\"" +
>> Constants.APP_NAME + "\"")
>>                         .build());
>>     }
>>
>>     private void abortWithForbidden(ContainerRequestContext
>> requestContext) {
>>
>>
>> requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
>>     }
>>
>>     static Permission getPermissionForResource(ResourceInfo resourceInfo)
>> {
>>
>>         Permission permission =
>> extractPermission(resourceInfo.getResourceMethod());
>>         if (permission != null) {
>>             return permission;
>>         }
>>         permission = extractPermission(resourceInfo.getResourceClass());
>>         return permission;
>>
>>     }
>>
>>     // Extract Permission from the annotated element
>>     static Permission extractPermission(AnnotatedElement
>> annotatedElement) {
>>
>>         if (annotatedElement == null) {
>>             return null;
>>         }
>>         Secured secured = annotatedElement.getAnnotation(Secured.class);
>>         return secured == null ? null : secured.value();
>>     }
>>
>>     private static class InternalSecurityContext implements
>> SecurityContext {
>>
>>         private final boolean secure;
>>
>>         private final SubjectDetails subjectDetails;
>>
>>         InternalSecurityContext(SubjectDetails subjectDetails, boolean
>> secure) {
>>
>>             this.subjectDetails = subjectDetails;
>>             this.secure = secure;
>>         }
>>
>>         @Override
>>         public Principal getUserPrincipal() {
>>
>>             return subjectDetails;
>>         }
>>
>>         @Override
>>         public boolean isUserInRole(String role) {
>>
>>             // We are not using role base authorization
>>             return true;
>>         }
>>
>>         @Override
>>         public boolean isSecure() {
>>
>>             return secure;
>>         }
>>
>>         @Override
>>         public String getAuthenticationScheme() {
>>
>>             return AUTHENTICATION_SCHEME;
>>         }
>>
>>     }
>>
>>     /**
>>      * For test purposes
>>      *
>>      * @param jwtService
>>      * @return
>>      */
>>     public static TokenAuthenticationFilter createTestInstance(JwtService
>> jwtService) {
>>
>>         TokenAuthenticationFilter filter = new
>> TokenAuthenticationFilter();
>>         filter.jwtService = jwtService;
>>         return filter;
>>     }
>> }
>>
>
> The Secured annotation is used to mark the methods that need to be
> protected and the required permission.
>
> @NameBinding
>> @Retention(RetentionPolicy.RUNTIME)
>> @Target({ ElementType.TYPE, ElementType.METHOD })
>> public @interface Secured {
>>
>>     Permission value() default Permission.AUTHENTICATED;
>> }
>>
>
> João Assunção
>
> Email: joao.assuncao@exploitsys.com
> Mobile: +351 916968984
> Phone: +351 211933149
> Web: www.exploitsys.com
>
>
>
>
> On Wed, Jun 22, 2022 at 8:54 PM Matthias Leinweber <
> m.leinweber@datatactics.de> wrote:
>
>> Hello Karaf user,
>>
>> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running without
>> success. 2.0.1 with installed cxf is working fine. But in 1.0.7-1.0.10.
>> http:list does not show a servlet and my jaxrs whiteboard resources are not
>> working.
>>
>> Any hints which component needs to be installed? Reading BNDrun +
>> Components DSL is pretty confusing.
>>
>> Btw, does anyone have a good example for authentication? Shiro seems to
>> be a bit overkill.
>>
>> br,
>> Matthias
>>
>

Re: aries-jax-rs-whiteboard

Posted by João Assunção <jo...@exploitsys.com>.
Hello Matthias,

Regarding authentication I normally use a ContainerRequestFilter.
Follows an example of a filter where authentication is done using a JWT
token.
It uses JwtService that is responsible for creating and validating tokens.
It uses jose4j.


@Secured
> @Provider
> @Priority(Priorities.AUTHENTICATION)
> @Component(scope = ServiceScope.PROTOTYPE, //
>         service = { ContainerRequestFilter.class }, //
>         property = {
>                 JaxrsWhiteboardConstants.JAX_RS_EXTENSION + "=" + true, //
>                 JaxrsWhiteboardConstants.JAX_RS_NAME + "=" +
> "TokenAuthenticationFilter", //
>                 Constants.JAX_RS_APPLICATION_SELECT, //
>         })
> public class TokenAuthenticationFilter implements ContainerRequestFilter {
>
>     private static final Logger LOGGER =
> LoggerFactory.getLogger(TokenAuthenticationFilter.class);
>
>     private static final String AUTHENTICATION_SCHEME = "Bearer";
>
>     private static final String AUTHENTICATION_SCHEME_PREFIX =
> AUTHENTICATION_SCHEME + " ";
>
>     @Reference(cardinality = ReferenceCardinality.MANDATORY)
>     JwtService jwtService;
>
>     @Context
>     private ResourceInfo resourceInfo;
>
>     @Override
>     public void filter(ContainerRequestContext requestContext) throws
> IOException {
>
>         // Get the Authorization header from the request
>         String authorizationHeader =
> requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
>         LOGGER.debug("authorizationHeader {}", authorizationHeader);
>         // Validate the Authorization header
>         if (!isTokenBasedAuthentication(authorizationHeader)) {
>             LOGGER.info("Missing auth token. Aborting");
>             abortWithUnauthorized(requestContext);
>             return;
>         }
>         // Extract the token from the Authorization header
>         final String token =
> authorizationHeader.substring(AUTHENTICATION_SCHEME_PREFIX.length());
>         try {
>             // Validate the token. An exception will be thrown if invalid
>             SubjectDetails subjectDetails =
> jwtService.validateToken(token);
>             LOGGER.debug("Token for subject {} accepted",
> subjectDetails.getId());
>             initSecurityContext(requestContext, subjectDetails);
>             Permission requiredPermission =
> getPermissionForResource(resourceInfo);
>             if (!subjectDetails.hasPermission(requiredPermission)) {
>                 LOGGER.debug("subject {} lack permission {}",
> subjectDetails.getId(), requiredPermission);
>                 abortWithForbidden(requestContext);
>             }
>         }
>         catch (AuthenticationException e) {
>             LOGGER.trace("Ignored", e);
>             LOGGER.info("Token validation failed. Aborting");
>             abortWithUnauthorized(requestContext);
>         }
>     }
>
>     /**
>      * @param requestContext
>      * @param subjectDetails
>      */
>     private void initSecurityContext(ContainerRequestContext
> requestContext, SubjectDetails subjectDetails) {
>
>         final SecurityContext currentSecurityContext =
> requestContext.getSecurityContext();
>         boolean secure = currentSecurityContext.isSecure();
>         requestContext.setSecurityContext(new
> InternalSecurityContext(subjectDetails, secure));
>
>     }
>
>     private boolean isTokenBasedAuthentication(String authorizationHeader)
> {
>
>         // Check if the Authorization header is valid
>         // It must not be null and must be prefixed with "Bearer" plus a
>         // whitespace
>         // The authentication scheme comparison must be case-insensitive
>         return StringUtils.startsWithIgnoreCase(authorizationHeader,
> AUTHENTICATION_SCHEME_PREFIX);
>     }
>
>     private void abortWithUnauthorized(ContainerRequestContext
> requestContext) {
>
>         // Abort the filter chain with a 401 status code response
>         // The WWW-Authenticate header is sent along with the response
>         requestContext.abortWith(
>                 Response.status(Response.Status.UNAUTHORIZED)
>                         .header(HttpHeaders.WWW_AUTHENTICATE,
>                                 AUTHENTICATION_SCHEME + " realm=\"" +
> Constants.APP_NAME + "\"")
>                         .build());
>     }
>
>     private void abortWithForbidden(ContainerRequestContext
> requestContext) {
>
>
> requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build());
>     }
>
>     static Permission getPermissionForResource(ResourceInfo resourceInfo) {
>
>         Permission permission =
> extractPermission(resourceInfo.getResourceMethod());
>         if (permission != null) {
>             return permission;
>         }
>         permission = extractPermission(resourceInfo.getResourceClass());
>         return permission;
>
>     }
>
>     // Extract Permission from the annotated element
>     static Permission extractPermission(AnnotatedElement annotatedElement)
> {
>
>         if (annotatedElement == null) {
>             return null;
>         }
>         Secured secured = annotatedElement.getAnnotation(Secured.class);
>         return secured == null ? null : secured.value();
>     }
>
>     private static class InternalSecurityContext implements
> SecurityContext {
>
>         private final boolean secure;
>
>         private final SubjectDetails subjectDetails;
>
>         InternalSecurityContext(SubjectDetails subjectDetails, boolean
> secure) {
>
>             this.subjectDetails = subjectDetails;
>             this.secure = secure;
>         }
>
>         @Override
>         public Principal getUserPrincipal() {
>
>             return subjectDetails;
>         }
>
>         @Override
>         public boolean isUserInRole(String role) {
>
>             // We are not using role base authorization
>             return true;
>         }
>
>         @Override
>         public boolean isSecure() {
>
>             return secure;
>         }
>
>         @Override
>         public String getAuthenticationScheme() {
>
>             return AUTHENTICATION_SCHEME;
>         }
>
>     }
>
>     /**
>      * For test purposes
>      *
>      * @param jwtService
>      * @return
>      */
>     public static TokenAuthenticationFilter createTestInstance(JwtService
> jwtService) {
>
>         TokenAuthenticationFilter filter = new TokenAuthenticationFilter();
>         filter.jwtService = jwtService;
>         return filter;
>     }
> }
>

The Secured annotation is used to mark the methods that need to be
protected and the required permission.

@NameBinding
> @Retention(RetentionPolicy.RUNTIME)
> @Target({ ElementType.TYPE, ElementType.METHOD })
> public @interface Secured {
>
>     Permission value() default Permission.AUTHENTICATED;
> }
>

João Assunção

Email: joao.assuncao@exploitsys.com
Mobile: +351 916968984
Phone: +351 211933149
Web: www.exploitsys.com




On Wed, Jun 22, 2022 at 8:54 PM Matthias Leinweber <
m.leinweber@datatactics.de> wrote:

> Hello Karaf user,
>
> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running without
> success. 2.0.1 with installed cxf is working fine. But in 1.0.7-1.0.10.
> http:list does not show a servlet and my jaxrs whiteboard resources are not
> working.
>
> Any hints which component needs to be installed? Reading BNDrun +
> Components DSL is pretty confusing.
>
> Btw, does anyone have a good example for authentication? Shiro seems to be
> a bit overkill.
>
> br,
> Matthias
>

Re: aries-jax-rs-whiteboard

Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi Matthias,

Which Karaf version are you using ?

Aries JAX RS is tested in Karaf itests but due to some changes in
Aries JAX-RS whiteboard, we didn't update yet.
You can take a look on the example:
https://github.com/apache/karaf/tree/main/examples/karaf-rest-example

I created https://issues.apache.org/jira/browse/KARAF-7455 to upgrade
Aries JAX-RS whiteboard.

Regards
JB

On Wed, Jun 22, 2022 at 9:53 PM Matthias Leinweber
<m....@datatactics.de> wrote:
>
> Hello Karaf user,
>
> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running without success. 2.0.1 with installed cxf is working fine. But in 1.0.7-1.0.10. http:list does not show a servlet and my jaxrs whiteboard resources are not working.
>
> Any hints which component needs to be installed? Reading BNDrun + Components DSL is pretty confusing.
>
> Btw, does anyone have a good example for authentication? Shiro seems to be a bit overkill.
>
> br,
> Matthias