{"id":2636,"date":"2018-07-17T12:24:02","date_gmt":"2018-07-17T10:24:02","guid":{"rendered":"http:\/\/hasselba.ch\/blog\/?p=2636"},"modified":"2018-07-17T12:28:04","modified_gmt":"2018-07-17T10:28:04","slug":"dropping-dominos-http-task-2-running-in-user-context","status":"publish","type":"post","link":"https:\/\/hasselba.ch\/blog\/?p=2636","title":{"rendered":"Dropping Domino\u2019s HTTP task (2): Running in User Context"},"content":{"rendered":"<p>To use <a href=\"https:\/\/hasselba.ch\/blog\/?p=2625\">the approach<\/a> as an alternative to Domino&#8217;s HTTP task, we need support for the different user contexts, because using <em>NotesFactory.createSession()<\/em> just creates a session for the current Notes ID used.<\/p>\n<p>This goal can be achived by using the Java NAPI and the following method:<\/p>\n<pre><code>import com.ibm.domino.napi.NException;\r\nimport com.ibm.domino.napi.c.NotesUtil;\r\nimport com.ibm.domino.napi.c.xsp.XSPNative; \r\n\r\n\/**\r\n* create a new Domino session for the give username\r\n* @param userName\r\n* String containing the canonical username\r\n* @return\r\n* lotus.domino.Session for the given username\r\n* @throws NException\r\n* @throws ServletException\r\n*\/\r\npublic static Session createUserSession(final String userName)\r\n{\r\n   Session session = null;\r\n   try {\r\n      long hList = NotesUtil.createUserNameList(userName);\r\n      session = XSPNative.createXPageSession(userName, hList,\r\n         false, false);\r\n\r\n      return session;\r\n   } catch (Exception e) {\r\n      e.printStackTrace();\r\n   }\r\n\r\n   return session;\r\n}\r\n<\/code><\/pre>\n<p>It is required to load the required <em>njnotes.dll<\/em> before this method can be used, otherwise the C-API references won&#8217;t work. This must be done after initiation of the NotesThread and (only once) per thread:<\/p>\n<pre><code>System.loadLibrary(\"njnotes\");<\/code><\/pre>\n<p>It is also required to use the full hierarchical name for the session creation, otherwise readers \/ authors won&#8217;t work correctly (you can create a session for every user you want, even for not existing ones).<\/p>\n<p>To get this work correctly, I have created a little helper class which memorizes if the Notes-relevant part was already initialized for the thread used:<\/p>\n<pre><code>public class Utils {\r\n\r\n   private static final ThreadLocal&lt;Boolean&gt; isNotesInitialized = new ThreadLocal&lt;Boolean&gt;();\r\n\r\n   public static void initNotes(){\r\n\r\n      \/\/ check if the Thread is already initialized\r\n      if( Boolean.TRUE.equals( isNotesInitialized.get() ) )\r\n         return;\r\n\r\n      \/\/ init Notes\r\n      NotesThread.sinitThread();\r\n      System.loadLibrary(\"njnotes\");\r\n\r\n      \/\/ mark as initialized\r\n      isNotesInitialized.set( Boolean.TRUE );\r\n\r\n   }\r\n\r\n}<\/code><\/pre>\n<p>Now let&#8217;s activate <a href=\"https:\/\/spring.io\/guides\/gs\/securing-web\/\">Spring Boot Security<\/a> on the new server by adding the required dependencies to the <em>pom.xml<\/em>:<\/p>\n<pre><code>&lt;!-- https:\/\/mvnrepository.com\/artifact\/org.springframework.boot\/spring-boot-starter-security --&gt;\r\n&lt;dependency&gt;\r\n   &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n   &lt;artifactId&gt;spring-boot-starter-security&lt;\/artifactId&gt;\r\n&lt;\/dependency&gt;<\/code><\/pre>\n<p>To use the authentication with the existing Domino environment, we can use our own <em>AuthenticationProvider<\/em>:<\/p>\n<pre><code>import org.springframework.security.authentication.AuthenticationProvider;\r\nimport org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\r\nimport org.springframework.security.core.Authentication;\r\nimport org.springframework.security.core.AuthenticationException;\r\nimport org.springframework.stereotype.Component;\r\n\r\nimport org.springframework.security.authentication.BadCredentialsException;\r\n\r\n@Component\r\npublic class CustomAuthenticationProvider implements AuthenticationProvider {\r\n\r\n   @Override\r\n   public Authentication authenticate(Authentication authentication) throws AuthenticationException {\r\n      String name = authentication.getName();\r\n      String password = authentication.getCredentials().toString();\r\n\r\n      String realUser = validatePassword( name, password );\r\n\r\n      if( Utils.isEmptyString( realUser ) ){\r\n         throw new BadCredentialsException(\"Authentication failed for user = \" + name);\r\n      }\r\n\r\n      return auth;\r\n   }\r\n\r\n   @Override\r\n   public boolean supports(Class&lt;?&gt; authentication) {\r\n      return authentication.equals(UsernamePasswordAuthenticationToken.class);\r\n   }\r\n\r\n   public String validatePassword( final String userName, final String password){\r\n      \/\/ see https:\/\/github.com\/hasselbach\/domino-stateless-token-servlet\/blob\/master\/src\/ch\/hasselba\/servlet\/DominoStatelessTokenServlet.java\r\n      \/\/ and convert the username to the hierarchical one\r\n   }\r\n}<\/code><\/pre>\n<p>If the authentication was successfull, we can access the correct username from the <em>Principal<\/em> object. Just add it as a parameter to the controller method, and you get the hierarchical name.<\/p>\n<pre><code>@MessageMapping(\"\/hello\")\r\n@SendTo(\"\/topic\/greetings\")\r\npublic Greeting greeting(HelloMessage message, Principal principal) throws Exception {\r\n\r\n   Thread.sleep(1000); \/\/ simulated delay\r\n\r\n   \/\/ init Notes\r\n   Utils.initNotes();\r\n\r\n   \/\/ create the session for the user\r\n   Session session = (Session) Utils.createUserSession(principal.getName());\r\n\r\n   return new Greeting(\"Hello, \" + HtmlUtils.htmlEscape( session.getEffectiveUserName() ) + \"!\" );\r\n}<\/code><\/pre>\n<p>Now let&#8217;s see this in action:<\/p>\n<p><video controls=\"controls\" width=\"100%\" height=\"100%\"><source src=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/Domino HTTP.mov\" type=\"video\/quicktime\" \/><source src=\"https:\/\/hasselba.ch\/blog\/wp-content\/uploads\/2018\/07\/Domino HTTP.mp4\" type=\"video\/mp4\" \/><\/video><\/p>\n<p>P.S.<br \/>\nI have ignored recycling and termination of the Notes threads for an easier understanding.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To use the approach as an alternative to Domino&#8217;s HTTP task, we need support for the different user contexts, because using NotesFactory.createSession() just creates a session for the current Notes ID used. This goal can be achived by using the &hellip; <a href=\"https:\/\/hasselba.ch\/blog\/?p=2636\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89,82,81],"tags":[31,16,127,12],"class_list":["post-2636","post","type-post","status-publish","format-standard","hentry","category-java","category-server","category-web","tag-java","tag-server","tag-spring-boot","tag-web"],"_links":{"self":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2636","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2636"}],"version-history":[{"count":9,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2636\/revisions"}],"predecessor-version":[{"id":2647,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/2636\/revisions\/2647"}],"wp:attachment":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2636"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2636"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2636"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}