Saturday, 23 March 2019

Context Aware configuration in AEM


Configurations are the vital part of any project. The decision on how to handle and maintain this can be considered as the most crucial part in the development activity. In AEM we can handle configurations in different ways. A simple OSGI configuration can be considered for most number of use cases where environment specific values are not required. It is not an ideal way to go for OSGI configuration when we have third party integrations that expose different urls for different environments. This will hamper the process of regular CI/CD activity as we need to manually change the configurations after each deployment in different environments. We can choose run mode configuration in this use case. Just like that I want to talk about another method of handling configuration, the context aware configuration.

First let me write about the implementation and at the later stage I’ll help you with some use cases.
Just like the name this configuration can be applied based on the context. Let’s say if you want to apply some set of values under the path /content/abc and an entirely different set of values under the path /content/xyz, all you have to do is to keep two set of configurations by following the same hierarchy under the conf folder. In a traditional OSGI config approach we’ll keep all the configuration values under one place and programmatically we’ll check the path url and corresponding property value will be taken. Here also we keep the configurations in the node type sling:Osgiconfig but separately, based on context path. The sling:configRef jcr property with value of configuration path under the content page node will be used to pick the property values.




In the screenshot above you can see that I’ve created three configurations with the context path cntxtaware/content/en/sling:configs, cntxtaware/content/en/ca-level1/sling:configs and cntxtaware/content/fr/sling:configs. These property values will be available once it is called using the jcr property sling:configRef from the content node.



The context aware configuration follow a child hierarchy, say for example all the pages under cntxtaware/fr will be getting the same property values that is configured under the context path ‘cntxtaware/content/fr/sling:configs’  even if the child pages do not have any sling:configRef property provided the page cntxtaware/fr is referred to the config path. The page ‘cntxtaware/content/en’ will take the property values from the config path ‘cntxtaware/content/en/ sling:configs’. The page ‘cntxtaware/content/en/ca-level1’ is configured with the config path ‘cntxtaware/content/en/ca-level1/sling:configs’. So the page ‘cntxtaware/content/en/ca-level1/ca-level2’ also inherit the parent configuration, that is ‘cntxtaware/content/en/ca-level1/sling:configs’.

In Java side this can be consumed just like a OSGI configuration.




ConfigurationBuilder api needs to be used to get the value. This can be applied on OSGI service, servlets or anywhere. In my example I am using a sling model to get the property value. So that I can easily add the component in my page to verify.



The results are blow.







I have a bad habit on creating separate repositories for different functionalities. One day I’ll combine all my work together in a single project until then you can access this project from the below github path.


This project is created using the archetype 15, so you’ll see the same template structure without test modules.
So where can we use this?
There are many use cases you can think about. When you have some third party applications to be integrated and they’ll provide you different token values for different regions or some logical implementation to disassociate certain hierarchy of pages with a different menu/navigation etc you can leverage the capability of context aware configuration.


Tuesday, 9 October 2018

How to upload with folders & subfolders along with files in to AEM DAM? How to make html files uploaded in the DAM to render in the browser rather than downloading?

I've worked with a POC task to find a solution to upload web pages (html's, css and js files) into AEM DAM. This is a migration activity from Sharepoint and these pages will be placed in folders & subfolders. OOB DAM doesn't support folder upload. But we can attain this by using the AEM desktop application. Upload folders and subfolders along with files by connecting AEM instance to the AEM desktop application.

Please go through these articles to get more information about the AEM desktop application.

https://helpx.adobe.com/experience-manager/desktop-app/release-notes.html
https://helpx.adobe.com/experience-manager/desktop-app/aem-desktop-app.html

To render this as html in the browser we need to make some configuration changes.

1) Need to remove the mime type text/html from Blacklisted Mime Types of Dam Safe Binary Filter. 2) We need to uncheck the Enable for All Resource paths from Apache Sling Content Disposition Filter. (Additional security features added in recent versions. So you may need to compromise on this part in the implementation)

Thursday, 31 May 2018

How to restrict the component edit operation for a set of users?

In the project I was working on had a requirement. Only a set of authors should be given permission to edit certain components and no authors should be given permission to delete any components. This is because the authors were not fully trained to use AEM during initial days. To realize this I followed two approach. In this blog I am explaining on how I did that.



1) Our first requirement was that we need to remove the delete button for certain components. For that we created 'cq:editConfig' node and included 'cq:actions' property. Inside that property we explicitilty gave operations which we need. In our case we have given 'edit','copymove' and 'insert'. This needs to be included on all components where we need to remove the delete button. This will cause the delete button to disappear from component author mode.



2) The next requirement was to restrict some authors from the component edit operation. For that we created two AD groups 'Author' and 'Admin'. Business users will be part of only 'Author' group and admin users will be part of 'Author' and 'Admin'. In our case all users were part of 'Author' group by default and we restricted all access including read, to the path where components reside for this group. In our case it was under 'apps/project-name/components/*'. We didn't restrict access to components path for 'Admin' group.

 In our application the authentication happens via SAML and when user which is part of only the 'Author' group cannot see the edit option for components. Yes the spanner button won't be visible. When an admin user log in, the edit option will be there because he is part of 'Admin' group which allows access to the component path.  

Wednesday, 14 March 2018

Handle SAML response in AEM

I got an opportunity to work on an AEM project, which implements SSO. For SSO implementation in AEM there are many admin level configurations need to be done. In this post I won't be talking about those because there are ample examples available in the internet. In many cases we may need to get the SAML attribute values from the SAML response and then process that for business cases. Here I'll be talking about on how to get those values.

The basic idea of the SSO is to provide access to a secured website from a different website where the user is already logged in. There are many admin related things to be done to implement this. We are not going to talk about how doing that. Here I only talk about from a development perspective. Before that let me talk about the background in a nutshell. Suppose we have two websites 'a.com' and 'b.com'. Both these websites are SSO protected. The user already logged in to the website 'a.com'. Our intention is to access 'b.com' from 'a.com' without asking any credentials. To implement this another path need to be configured which is outside the secure Auth protected path. Suppose that is '/content/b-redirect'. This path should be the url linking from 'a.com' to 'b.com'. Either you can create a servlet for this path or you can directly redirect from there to 'b.com' using any client script.. So when the user clicks the link it'll first go to the '/content/b-redirect' path and from there after applying any logic, it'll be redirected to 'b.com'. Make sure that the '/content/b-redirect' path is not SSO protected. Make sure that the AEM SAML Authentication Handler is configured properly and trust certificate is also placed. After the redirect a SAML request will be redirected to the IDP and it'll be authenticated providing a SAML response. Now we'll be successfully logged in to the 'b.com'.            

As you know that if all the SSO configurations are correctly done, then we can expect the encoded SAML response to hit AEM along with the HttpRequest. Most probably it would be configured as 'saml_login' path. These need to be processed at the beginning because the 'saml_login' will be lost on subsequent requests. So most people will use Filters or create a service by implementing AuthenticationInfoPostProcessor. This is the traditional approach to process the SAML response. This implementation requires to decode and loop through the SAML XMl. The needed attribute values can be stored in any cookie or can be used to develop business logic based on your requirement. The main problem I identified in this approach is that the Filter and the service will be invoked in all subsequent requests. It is not necessary that the request should be a click event. Even calling the client library will invoke this. Of course execution of functionality can be prevented by adding conditional checks like whether 'saml_login' is still with the request before the execution of functionality. We have a different way of doing this in AEM. I have done that in AEM 6.2. I am not sure about whether this would be applicable in lower versions of AEM.

In this post I am really concerned about the multifield Synchronized Attributes. In this field you can add expected SAML attributes as configurations. For example it can be included as 'userType = profile/userType', userRole=profile/userRole etc. The good thing is that now the SAML attribute value userType and userRole will be saved under the user profile node as userType and userRole field. We can access this node property from our code using 'userProperties' API. The overhead of decoding , parsing and looping of SAML response XML can be avoided using this approach. Anytime these values can be accessed.

The user profile node properties can be accessed by the below code snippet. The slingRequest object should be adapted to the UserProperties type.

UserProperties userProps = request.adaptTo(UserProperties.class);

try {

setUserName(userProps.getProperty("userType"));

setFirstName(userProps.getProperty("userRole"));

} catch (RepositoryException e) {

}

However, in certain situations we need to rely on the traditional way of handling SAML response. The limitation of the above approach is that if any configured SAML attribute values become empty then the authentication will fail. In my project I had a situation like when a corporate user logs in they don't have any value for one particular attribute but for other users that has value. This value is significant for our business case as we are using this value as a parameter for a service API call for the non corporate user. So we must have to configure that in the Synchronized Attributes field. In those situation there are two ways to solve.

1) Configure the IDP to pass a default value for empty SAML attribute. By this we can avoid Authentication failure at AEM side. In the code we can implement logic to check the default value. It is easier said than done because any changes done in the IDP may affect many applications. So other applications logic may also need to be changed. In most scenarios this can't be practical.

2) Configure the IDP not to pass empty attribute. This is something more sensible. I can't find any value addition in passing empty value SAML attribute along with the SAML response. If the configured SAML attribute is not available in the response synchronisation won't occur and authentication doesn't fail on AEM side. You may need to convince the business to follow this approach.

Unfortunately, if both these approaches don't work for you then you will be left only to follow the traditional method.

Friday, 26 January 2018

AEM Run mode configuration using OSGI R6 annotations

In my previous project I tried to migrate run mode configuration written using scr annotations to OSGI R6 annotations. Unfortunately I couldn't find anything useful for me in the internet as most of the articles were describing only about normal configuration migration. As I am not an expert I couldn't easily find a solution to do that by referring those articles. In this blog I am writing on how I have achieved that.

This is not a step by step tutorial. I am not good at writing those. This post only help those who already know about OSGI run mode configurations and have implemented those using SCR annotations. This may guide those who are facing challenges while migrating. My github link only contains the OSGI service and its implementation. Building that gives you a jar which can be deployed directly using the AEM system/console/bundles interface(As I mentioned earlier this is not a project, just a sample code to guide). Please go through the Adobe documentation
https://helpx.adobe.com/experience-manager/using/osgi_config.html. I was trying to implement the same thing using OSGI R6 annotations as SCR annotations they are using in the documentation is now deprecated. Apart from the Java code part where the OSGI service is implemented you can refer the rest of the article to implement this.

This is my github link to the code sample. https://github.com/thatsmeadarsh/aem-osgiR6-annotaion-runmode-conf-demo