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