Showing posts with label DSAPI. Show all posts
Showing posts with label DSAPI. Show all posts

Monday, November 22, 2021

Alter user during authentication using DSAPI

I had a need to alter user during web-authentication process on fly (skipping password validation). Initially the task looked impossible but I managed to solve it using DSAPI filter.
Though the solution looks quite unsecure it could be very useful in some cases (by high level administrators) who needs to 'signin' as a user in their organization to do some checks.

Here are few most important snippets how to do that:

1. Subscribe for the event kFilterAuthenticate

That means that our dsapi filter only intercepts one specific event: kFilterAuthenticate), as there are other 10-15 other events which we do not wanna touch.

EXPORT unsigned int FilterInit(FilterInitData* filterInitData) {
	STATUS   error = NOERROR;

	filterInitData->appFilterVersion = kInterfaceVersion;
	filterInitData->eventFlags = kFilterAuthenticate;

	// other logic
	// ...
}

2. Catch the authenticate event and process it

Get our event and associate it with a C function

EXPORT unsigned int HttpFilterProc(FilterContext* context, unsigned int eventType, void* eventData) {
	/* Include only those events we want to handle */
	switch (eventType) {
	case kFilterAuthenticate:
		return Authenticate(context, (FilterAuthenticate *) eventData);
	default:
		break;
	}

   return kFilterNotHandled;
}	// end HttpFilterProc

3. Finally set a desired username

Below I only show the key moment - replace user name with another name

unsigned int Authenticate(FilterContext* context, FilterAuthenticate* authData) {
	/* logic that calculate username  */
    // .................................
    // char[] fullName = "CN=T5 Tester5/O=DmytroDev";
    // .................................

	/* Copy the canonical name for this user that dsapi requires.  */
	strncpy ((char *)authData->authName, fullName, authData->authNameSize);
	authData->authNameSize = strlen(alterAuthToken);
	authData->authType = kAuthenticBasic;
	authData->foundInCache = TRUE;

	return kFilterHandledEvent;
}

In order to improve security I have built an application on Domino side that generates tokens which have to be set in cookie and then DSAPI filter reads the cookie and get username from database. Tokens could be generated only by certain people are will be deleted by schedule agents after some time.



On the screenshot below you can see that I signed in as a "T5 Tester5" using my custom token AlterAuthToken while I am Anonymous.



Monday, February 17, 2020

Rewrite URL with CloudFlare for Domino

Years ago I created few solutions for Domino using DSAPI:
  1. remove last slash in URL served by Domino.
  2. rewrite URL.
  3. better control over 404/500 error pages.

It was quite complicated solution (DSAPI is not easy topic).
Today another client asked similar features (remove last slash and rewrite url).
I started to recall how DSAPI works but then I reminded myself that the client stick with CloudFlare in front of their Domino servers.

Cloudflare has 'page rules' which allow to solve issue with last trailing slash. Just matter of configuration.

And about rewriting URL it's actually possible to achieve with workers! You can see below how to rewrite URL.
In example below I changed url like
domain.com/section/page?param1=aaa&param2=bbb
=>
domain.com/router?openagent&req=section/page&param1=aaa&param2=bbb

addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  const pathname = url.pathname.substr(1);
  if (pathname.startsWith("design") || pathname.startsWith("files") || pathname.startsWith("api")) {
    return;
  }
  event.respondWith(handleRequest(event.request));
})

/**
 * Rewrite URL and makes query param available
 * @param {Request} request
 */
async function handleRequest(request) {
  let url = new URL(request.url);
  let pathname = url.pathname.substr(1);
  url.pathname = "?openagent&req="+pathname;
  var query = url.search;
  if (query!="") {
    url.pathname += "&" + query.substr(1);
  }

  const newRequest = new Request(url, new Request(request));
  return await fetch(newRequest)
}

Tuesday, January 05, 2016

DSAPI for Domino

This post is about to make short summary about DSAPI filter. I've finished my project some time ago and it looks like I'm not going to do any work with it in near future.

What is DSAPI?

The Domino Web Server Application Programming Interface (DSAPI) is a C API that lets you write your own extensions to the Domino Web Server. DSAPI extensions, or filters, are notified whenever a particular event occurs during the processing of a request.
I've written few articles about DSAPI before and now it is time to publish my work/project on github. You can find it here: domino-dsapi-handler. I did not have time to make a documentation for it (and I'm not sure I will do it, without funding), but all sources are there.

In this post I will highlight most important things you need to know if you are going to build your own DSAPI filter.

Initialization

Domino calls the initialization function when the filter is loaded. The filter is loaded when the Domino HTTP server task is started or when the HTTP task is restarted with the Domino console command 'tell http restart'.
 DLLEXPORT unsigned int FilterInit(FilterInitData* filterInitData) {  
  // init events you want to handle  
  filterInitData->eventFlags = kFilterRewriteURL | kFilterResponse | kFilterTranslateRequest;  
  // another code  
  return kFilterHandledRequest;  
 } // end FilterInit  

Terminate filter

The filter may also define a termination entry point. Domino will call this function whenever the filter is about to be unloaded. The filter can use this function to clean up resources it allocated
 DLLEXPORT unsigned int TerminateFilter(unsigned int reserved) {  
  return kFilterHandledEvent;  
 }     // end TerminateFilter  

HttpFilterProc

The Event Notification function does the actual work of the filter. Domino calls the Event Notification function whenever a particular event occurs during the processing of an http request. When Domino calls the filter's Event Notification function it passes information about the request and the event being processed. On each call the filter can decide to handle the event, with or without an error return, or decline to handle the event.
 DLLEXPORT unsigned int HttpFilterProc(FilterContext* context, unsigned int eventType, void* eventData) {  
      switch (eventType) {  
           case kFilterTranslateRequest:  
                return QueryRewrite(context, (FilterMapURL *) eventData);  
           case kFilterRewriteURL:  
                return RewriteURL(context, (FilterMapURL *) eventData);  
           case kFilterResponse:  
                return ResponseHeaders(context, (FilterResponseHeaders *) eventData);  
           default:  
                return kFilterNotHandled;  
      }  
 }     // end HttpFilterProc  

The rest of logic you can read yourself in SEOUrlHandler.c file. I've added information about how to compile DLL in readme on github. However If you have any question regarding some details let me know either via email or as a comment to this post. I will be glad to help.

Related topics
Rewriting URL in Domino using DSAPI
Solution for Lotus Domino to the trailing slash problem
Replacement for DSAPI in Java/XPages

Used materials
Domino Web Server Application Interface (DSAPI)

Friday, July 12, 2013

Replacement for DSAPI in Java/XPages

I've written few articles about how DSAPI could help you to control classic web application built on Domino. That solution worked perfect for us and difficulties I encountered were:
  • slower development process due to my skill in C;
  • poor documentations about how things work in DSAPI (probably most weak side for me);
  • deployment process (you need to create new DLL each time and upload it to Domno Server, then restart HTTP);
  • you have to be 'very-very' careful with everything, one mistake (i.e. memory leak somewhere) can crash the server at some point;

Last months we worked on new Web CMS based on Java/Velocity in Domino and result I must say was really great. I will make post about most interesting things later: the topic will be about java, html/templates, velocity, git, jenkins, jira and how it works together. I would call it pure Java approach to do development in Domino. The beauty using java as engine allow us to get rid of DSAPI. So everything what have been done with DSAPI (and in total I spend maybe 2-3 weeks) we replaced in 2-3 hours with Java.

case #1: Re-write URL from http://domain/page/subpage/ to http://domain/page/subpage with 301 status. We simply set new location in header and new status
  getResponse().setStatus(MOVED_PERMANENTLY_STATUS);
  getResponse().setHeader(LOCATION_HEADER, uri);
case #2: 404/500 etc error pages we only set correct status for response + throw out required content
  getResponse().setStatus(DEFAULT_ERROR_STATUS);

So my feeling about DSAPI is actually quite good, however be sure you know how to cook it, otherwise - don't go with that solution. Remember my example: I've spent weeks doing DSAPI via C and now we did exactly same in few hours. That feeling when I compared what I spent with DSAPI (via C) compare to new solution with Java. 100 hours agains 3.


Related topics
DSAPI for Domino
Rewriting URL in Domino using DSAPI
Solution for Lotus Domino to the trailing slash problem

Wednesday, March 06, 2013

Rewriting URL in Domino using DSAPI

I will briefly describe what we were aiming to achieve. In order to open page with parameters Domino requires to add ?open or ?opendocument action and only after that Domino allows to add parameters. It's quite annoying for us due to some integration with another systems, those systems expect they can simply add parameters just after our pages, i.e.: http://www.host/page?parameter=123 etc. Also important reason - we simply do not like this ?open or ?opendocument in URL.

we want to be able to that:
www.host/page?opendocument&parameter=123 ==> www.host/page?parameter=123

I got couple helpful comments in my previous article about URL control in Domino, that helped me to look on different solutions, thanks guys. However I decided that those solutions a bit complicated to setup and they could do some impact on page load time.

I've been working on DSAPI solution last days and finally with some help I've made it! Now we have full control with our URLs, at least I've such feeling :). Let me share my small success to all of you.

Here you can find main steps you need to do. Please keep in mind, I've updated my code a bit as our logic has many rules when exactly to add parameters.

1. Enable flag to catch Rewrite URL event.
filterInitData->eventFlags = kFilterRewriteURL;
2. Link 'rewrite URL' event with you function.
DLLEXPORT unsigned int HttpFilterProc(FilterContext* context, unsigned int eventType, void* eventData) {
 switch (eventType) {
  case kFilterRewriteURL:
   return RewriteURL(context, (FilterMapURL *) eventData);
  default:
   return kFilterNotHandled;
 }
}
3. Finally the main logic, that makes URL rewriting.
int RewriteURL(FilterContext* context, FilterMapURL* pEventData) {
 FilterParsedRequestLine pReqData;
 unsigned int errid=0;

 // if there are no parameters in URL - nothing to do.
 if (strstr(pEventData->url, "?")==NULL) return kFilterNotHandled;
 // read request as we are going to update query.
 context->ServerSupport(context, kGetParsedRequest, &pReqData, NULL, NULL, &errid);
 // if query starts from opendocument - nothing to do
 if (strncmp(pReqData.pQueryUri, "opendocument", strlen("opendocument"))==0) return kFilterNotHandled;
 // adding opendocument before query and put result in pEventData->pathBuffer
 sprintf(pEventData->pathBuffer, "%s?opendocument&%s", pReqData.pPathUri, pReqData.pQueryUri);

 return kFilterHandledEvent;
}
Solution we did works like a charm and what is very important it does not affect page load time (we measured of course)

Related topics
DSAPI for Domino
Solution for Lotus Domino to the trailing slash problem
Replacement for DSAPI in Java/XPages

Wednesday, February 27, 2013

Fighting for control of URL in Domino

We have really annoying problem for years with our URLs on all websites based on Domino. We use classic approach because we want control all tags + we like jQuery more etc. Our problem is about mandatory action [?open | ?opendocument] for pages with parameters, so if you want to add parameters to your page (i.e. param1=123) you have to add [?open | ?opendocument] just after your URL and only then you are allowed to add parameters.

Not really a huge problem, however we want to use new tracking code from google. That tracking has problems with [?open | ?opendocument]: it does not work correctly in case if first parameter is ?open. So now its blocker for us and we want to fix it for all another cases as well. We want to have possibility get rid off ?open | ?opendocument

Question in few words: "is it possible to get this URL working?"
http://www.e-conomic.com/accountingsoftware?parameter=123
instead of this
http://www.e-conomic.com/accountingsoftware?open&parameter=123

We've tried redirect, substitutions - no success ofc :(.

Next in my queue is DSAPI and I'm sure it can solve that problem (at least from documentation) however I'm not able to do that due to my experience with DSAPI and very very low information in the web.

I've played with different events in DSAPI: kFilterStartRequest, kFilterRawRequest and kFilterRewriteURL and tried to change incoming URL from request but did not success with it. Does any of you guys can give me tips :) what I do wrong or maybe there is another solution how to achieve that quest?

Tuesday, January 24, 2012

Error pages in Domino

First of all I'd like to briefly describe few another ways we can use to manage error pages in Domino.
Also notice, I will definitely update post few times more after all.

1. Lazy solution $$ReturnGeneralError and MessageString
It's the most fast and easy solutions, it require to create 1 design element (form) $$ReturnGeneralError, style it and add MessageString somewhere to explain what is wrong
Benefits:
- I see only 1 advantage compare to another approaches, its time to implement. Once you create $$ReturnGeneralError it starts to work. So few clicks and few minutes to manage output UI and you have solution.
Disadvantages:
- works inside of application only. So if you have 10 web applications, you need to manage error page in each of it. However you can setup inheritance and manage all error pages from 1 place etc.
- not very flexible on my opinion as we can operate with form-design elements only.

2. HTTP response headers
Simply create error page as design element or as document (does not matter) and create rule for your website in Domino Directory database. It does not require special knowledge and it is simple to use.

Benefits:
- easy to manage
- path to Error page (means we can use different database to keep error pages)
- we can use both: Design Elements and Documents as error page.
Disadvantages:
- require minor Administration of Domino skills.
it works in the same way as client side redirection. i.e. user will see “blink” of standard 404 page before loading custom 404 page.

3. "Whole server" solution
Use HTTPMultiErrorPage property in the Notes.ini file, for example HTTPMultiErrorPage=/error.html. Create rule on the Domino server for /error.html to be substituted corresponding page.
Benefits:
Disadvatages:

4. DSPAI as error handler for Domino
Most complicated approach but also most flexible from my point of view. There is quite low information about how to use it. It requires knowledge of Notes C Api as well. If you want to see how it looks, here is very good exampe (it helped me a lot) on loggin using DSAPI
Company I worked in use DSAPI for handling URL already, and now we are near to implement error handling using DSAPI as well (it already implemented it on development server, so soon I will show you real links etc).
DSAPI allows us to catch responses Domino generated for users and we can replace output in case if responce 400, 404 or any another.

I will post most important part of logic here, just to how overview how DSAPI works and what is gives to us. Notice I've changed code a bit, to show only most important part of it (I will copy comments from Paul's example just to make things faster) 

1. initiation of filter and register for response event
/*
* FilterInit() - Required filter entry point. Called upon
* filter startup, which occurs when HTTP server task is started.
*/
DLLEXPORT unsigned int FilterInit(FilterInitData* filterInitData) {
   filterInitData->eventFlags = kFilterResponse;
}
so what we did there, say to filter that we want to process response events
2. Link events we registered with functions
/*
* HttpFilterProc() - Required filter entry point. Dispatches the event notifications to the appropriate functions for handling the events.
*/
DLLEXPORT DWORD HttpFilterProc(FilterContext *pContext, DWORD dwEventType, void *pEventData)
{
 switch (dwEventType) {
  case kFilterResponse:
   return Response(pContext, pEventData);
 }

 return kFilterNotHandled;
}
Now we can process Responses to users
3. This is how we process Response to users
int Response(FilterContext* context, FilterResponseHeaders* eventData) {
 int responce = eventData->responseCode;

 if (responce==404) {
  if (Send404(context) == TRUE) {
   return kFilterHandledRequest;
  }
 }
 return kFilterNotHandled;
}
4. Finally code for Send404() how we replace content to users
While you read code you need to know that when we load filter we also initiate a table with KEY-HTML arrays. Yes we keep in memory Error HTML pages with keys we need. Our Keys - domain, we handle error pages on domain level, means for domain1.com - is 1 error page, for domain2.com is another error page. But you can do it in another way its up to you.
int Send404(FilterContext* context) {
 FilterResponseHeaders         response;
 unsigned int   errID = 0;
 char     szBuffer[DEFAULT_BUFFER_SIZE]={0};
 char     pszErrorPage[ERRRO_PAGE_SIZE]={0};
 FilterParsedRequestLine pRequestLine;
 unsigned int   pErrID;
 int      i;

 // As we are returning the entire page to the browser, we can 
 // set the response code to 404 (so the domino web log will show the error)
 response.responseCode=404;
 response.reasonText="Bad Request";
 
 // get domain name (its our key), could be done faster? right now its simple walk via 10-20 documents which is fine for now
 context->ServerSupport(context, kGetParsedRequest, &pRequestLine, NULL, NULL, &pErrID);
 for(i=0; i<errorPagesCount;i++) {
  if (strcmp(errorKey[i], pRequestLine.pHostName)==0) {
   strcpy(pszErrorPage, errorHTML[i]);
   i=errorPagesCount;
  }
 }

 if(strlen(pszErrorPage)<10) {
  sprintf(szBuffer, "Error page on %s is very small, something wrong", pRequestLine.pHostName);
  writeToLog(CRITICAL_MSG, szBuffer);
  return FALSE;
 }

 sprintf(szBuffer, "Content-Type: text/html; charset=UTF-8\n\nContent-length: %i\n\n", strlen(pszErrorPage));
 response.headerText = szBuffer;

 if (context->ServerSupport(context, kWriteResponseHeaders, &response, 0, 0, &errID) != TRUE) {
  sprintf(szBuffer, "Error sending redirect, code: %d", errID);
  writeToLog(CRITICAL_MSG, szBuffer);
  return FALSE;
 }
    if (context->WriteClient(context, pszErrorPage, (unsigned int) strlen(pszErrorPage), 0, &errID) != TRUE) {
  sprintf(szBuffer, "Error sending redirect, code: %d", errID);
  writeToLog(CRITICAL_MSG, szBuffer);
  return FALSE;
 }
 return TRUE;
}
OK guys, I've shared most complicated part of this DSAPI for error handling, rest you have complete yourself (homework :)), but feel free to ask my help here if you need.
benefits:
- most flexible approach (at least from those which I know)
- our logic control everything we want and we clearly see what is going on
disadvantages:
- most complicated ways from all I described
- require knowledge of Notes C API
- I did not try yet this solution with Linux servers (but I know it is possible to do)
- it may crash your Domino server in case if you did you filter wrong (memory leak etc)


5. xPage error handling is on way (on pause actually) and would be nice if somebody help me with that. I've read few articles in past from Per Henrik: XPages custom 404 and error page and Controlling the HTTP response status code in XPages, I think they can be very useful for those who doing in xPages. We will try this approach as we have ongoing huge projects based on xPage. 

Hey:
- ask to update/more details to article
- notify me about issues
- let me know if you know better way to handle errors

strongly recommended as it can help to many developers in future.

Tuesday, November 01, 2011

Solution for Lotus Domino to the trailing slash problem

2 months ago I've posted article about solution which can solve our problem in Domino with last trailing slash in URL. As I mentioned in my previous post, Domino does not care about url with/without last trailing slash, both way would work for Domino. Well, some of you maybe even find this feature useful because it gives less problems, however what if we talk about SEO (Search Engine Optimisation)?

There is actually article from google about to slash or not to slash, where they explain that they handle similar URL with and without last trailing slash as 2 different URL, they also propose ways how to solve this. If you ask what is bad here, answer is quite simple: just image that each URL has it's power/score for google. In case if we have only 1 possible URL for our page all score go to it, otherwise we will split them (high score will go to URL which is more often used by people in web).

Now about solution we implemented on our website. We used DSAPI to solve it as it does not do affect speed so much as different solutions. If you want to read more deeply about technical staff, you may want to read this article: solution to the trailing slash problem.

Yes it works like it should, no impact to page load's speed which was "very-very" important to us. And I heared google start to love us a bit more after that.

Related topics
DSAPI for Domino
Rewriting URL in Domino using DSAPI
Replacement for DSAPI in Java/XPages

Tuesday, August 02, 2011

Domino resolve URL with and without trailing as same URL, wrong?

We are faced with a problem (for us) with Domino. The problem is that Domino processes those 2 URL as similar same URL:

http://host/0/unid
http://host/0/unid/

the difference in 'trailing' (for those who thinks in same way as Domino :]). Well, it would be not a problem at all if you do internal staff, even very useful, problems start when we talk about SEO.

It actually affects our websites, as f.x. google 'eats' 2 URL (I mentioned above) as 2 different URL so their 'value' will be split on 2 part + 2 URL with same content also very bad as it is duplicate.

We should be able to either make 301's from the NOT CORRECT to the CORRECT URLs, or respond with a 404 (not found). Both options would be good.

Does anybody know ways how to do it?

[update from 28 October 2011] we've found solution with DSAPI filter, here you can read Solution to the trailing slash problem in Lotus Domino