Multi-tentant - appserver - webspeed

Posted by bart.syryn on 21-Feb-2014 03:19

Hi Folks, We're checking what we need to change in our code to get the application Multi-tenant. I see that you need a client-principal to authanticate. But I don't understand how this works when you have a stateless-appserver. Example, the user logs in (we have username, password and domain/tenant). We pass that to the appserver. We don't understand what happens there. I assume that we need to pass back something to the client-side (what and what are the datatypes). But how does this work when the user is logged in and he works in the application. Everytime, when data is needed, there's an appserver call, do we need to change all the appserver-programs to get that program to the correct tenant ? We use stateless-appserver ... The same question with webspeed. There's a html-page with login, do we need to change anything in webspeed/speedscript ? Kind regards Bart Syryn

Posted by Michael Jacobs on 24-Feb-2014 12:25

Adding to Peter's information:  

The implications are that for state-reset and state-aware connection models the user-id (Client-Principal) only needs to be set once at connect time in the AppServer.   For the stateless and State-free connection models the user-id (Client-Principal) needs to be set on every request because each request will run in a different Agent process.

Posted by Bill Wood on 21-Feb-2014 05:13

There are basically two approaches, depending on where you are enforcing security and the need to have a Single-Sign -On solution.

The 'easiest' way to simply assert tenancy when you need it.   The application must be working as a SUPER-TENANT, but if that is the case you can use the SET-EFFECTIVE-TENANT("tenantname") when necessary.  (or use the TENANT-WHERE clause).  

For WebSpeed this might be sufficient.  The WebSpeed agents would be super-tenants, and you would at the start of every http request figure out the relevant tenant and set the effective tenancy.

If you want to have a full Single-Sign-On between an ABL client and an AppServer, you need to basically have the SET-DB-CLIENT(handleToCP) when you start processing a request.   In a Stateless AppServer, you would need to have a mechanism to persist the client-principal once you have created it.

One typcial way to do this is to have a sessionID that lets you recover a previously created and sericalized Client-Principal (C-P) object.

- At some point (e.g. Connection.p) create and seal the C-P object, and export it to a safe store (file or db) under the session:context-Id.

- on each appserver call (activate.p), use the session:context-id to find and import the C-P.

- use SET-DB-CLIENT(hCP) to assert tenancy.

There are some options.   I found a PUG Challenge presentation by BravePoint that I may help explain this:

pugchallenge.org/.../CPObjectPUG1.pdf

Posted by Michael Jacobs on 21-Feb-2014 05:47

I would like to expand on Bill's response and offer another viewpoint:

I would not recommend the SET-EFFECTIVE-TENANT or TENANT-WHERE use except in the case of a maintenance utility that is used to coordinate data between multiple tenants, which is what they were made for.  These methods are simply unsafe because you lose the tenant isolation the database gives you, it bypasses any internal database auditing, it will not work well with table/field access permissions, and you have to be careful of audits (who generally do not like a client running code in the context of a privileged user who can access any tenant's data).   Bill is correct that if you do not need secure multi-tenant support the SET-EFFECTIVE-TENANT could make your job easier.   If you want/need a secure multi-tenant environment, the Client-Principal is more work but a better choice.

The Client-Principal is simply a container (i.e token) of authenticated user information that the OE database connection can validate and subsequently use to coordinate setting the user-id, tenant-id, auditing user-id (and privileges), and database permissions in a single operation.   It is relatively easy to generate, but the challenge lies in which application architecture use case you apply it to, because it can be used in a variety of them.  

You say your user logs into a stateless application, but where do you pick up the correct user-id for each request the client executes?   Generally your application login code would generate the C-P and stores it in the some location where it can be easily referenced on subsequent requests.  Your application probably already records the user-id for subsequent requests.  Once a request starts you retrieve who the user is, their C-P, and use SET-DB-CLIENT(hCP).   How the stateless request knows which authenticated user it is (and therefore their C-P) can be done in a variety of ways, but the starting point is to look into leveraging what your application already does.

The Client-Principal is also a means of passing user identity from one OpenEdge component (such as an AppServer to AppServer) in the 11.x releases so that you use the same user identity consistently in multiple places.  Just in case this is in your future.

I hope this helps you decide which is the best path to take for your application.  

Posted by wmtwood on 21-Feb-2014 06:05

WRT:

I would not recommend the SET-EFFECTIVE-TENANT or TENANT-WHERE use except in the case of a maintenance utility that is used to coordinate data between multiple tenants

I did say that using SET-EFFECTIVE-TENANT was the 'easiest' thing, but I agree with Mike that it does mean you are:

  • bypassing user authentication (and therefore per-user auditing)
  • not following 'best practices' for security

Posted by bart.syryn on 21-Feb-2014 06:29

Thanks for the replies.

I see that the set-effective-tenant is the easiest way.  So in every .p on the appserver side, we would need to set the tenant, and that can be easily done.

But maybe it's better to do it the right way and work with CP.

We have our own table of users, so we don't use _user.

At this moment, when the appserver .p is called, some parameters go along (one is the userid).

But I don't understand working with the CP.  Correct me if I'm wrong.

So the user logs in and the first call to the appserver is made. In our login.p (on the appserver), we check if it's a know user and also check password.  If I understand it correct, at that point we should make the CP and store it in a DB or something else together with the session:context-id.

When the user is logged in, than we need to pass on every call to the appserver (.p) the session:context-id.  Lookup the ID and with the associated CP do SET-DB-CLIENT(CP).

I could also use the 'activate' possibility in the openedge management, so that's the .p that is run on every call to the appserver before the .p that actually gets the data, but how do I pass the session:context-id to that .p ?

Another question, if you use CP, than you need to set SET-DB-CLIENT in every call to the appserver (stateless), what's the difference at that point with SET-EFFECTIVE-TENANT ?

Kind regards,

Bart Syryn

Posted by Michael Jacobs on 21-Feb-2014 06:53

The Client-Principal technology can work with your own built-in user account tables with no problems.  In fact, that was its first use case.

Your login scenario is correct.  The choice of storage location can vary, and which one should be chosen base on your application's architecture.

If you are writing stateful applications, especially in a web environment, there is always something that does back and forth between the client and the server.  Because you are doing a login, I can assume the application architecture is stateful.   So yes, the login operation would return to the client the session-id and pass it to the server on subsequent requests.   The same process as is used in most web applications.

When the request is received from the client you do a one-time operation: the session-id passed by the client is used as a key into the C-P storage and the C-P retrieved, followed by calling SET-DB-CLIENT() which will then tell you if the C-P is valid, expired, and do all the other internal settings.  After SET-DB-CLIENT() successfully executed, all of your ABL code paths then execute in the context of that tenant without passing additional user information or code changes for new tenants.

The user logout, or session expiration then removes the C-P from your storage location.

The activate procedure is a good place to change user identities because it is a single location and satisfies the best practice that all security route through a single point before application code executes.  In the later 11.x releases there is a 'client-context-id' that is generated and exchanged between OpenEdge clients and the AppServers, which is there to support server-side caching of Client-Principals.  You use it like you would with a web browser and passing the http session-id back and forth.  The later 11.x releases also allow you to pass the Client-Principal itself between OpenEdge clients and the AppServer - which supports the client-side caching of Client-Principals.   Kind of like a web browser passing back and forth a cookie with the user's information in it.  The 'client-context-id' is generally kept in sync with the C-P:session-id, which makes it easier to adapt to different clients.

Yes, you call SET-EFFECTIVE-TENANT or SET-DB-CLIENT() one time at the start of each client request.   The difference: SET-EFFECTIVE-TENANT provides no security and you have to do all of the user to tenant lookup yourself; SET-DB-CLIENT() is secure and does all of the user-id to tenant resolution for you.

Again, your choice based on what requirements your application must meet.

Posted by Peter Judge on 24-Feb-2014 11:06

> Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at
> that point I can lookup the client-principal in activate.p.

Yes, exactly.

> With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-
> NAME = "tenantname". Is this correct ?

Yes (DOMAIN-NAME = 'domain' not 'Tenant' but close enough). Domain :: Tenant is a many-to-one relation.

>If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username,
>password and domain-name (tenant)).

Don't need to pass any extra handles for authentication? No. Note that you can – as an alternative – pass the actuall CLIENT-PRINCIPAL instead of the ClientContextID (see the doc below for more info).

>Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the
> help, but what is clientcontextid ?

Yes, the property is available in the ABL. Note that it is only available on the SESSION handle on the AppServer. On the client, you get at it from the server hander (ie hAppServer).

It's a property on the Progress.Lang.OERequestInfo object, which is what the CURRENT-REQUEST-INFO attribute holds. http://documentation.progress.com/output/OpenEdge113/oe113html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=ABL/ABL%20Reference/23dvref-Classes.037.41.html for moree

I do not know what the implications are for state aware AppServer modesl (-reset and friends).
 
-- peter
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Monday, 24 February, 2014 09:38
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

Thanks for all the responses.

Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at that point I can lookup the client-principal in activate.p.

With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-NAME = "tenantname". Is this correct ?

In a stateless-appserver, I need to read this information everytime there's a call to the appserver (activate.p) ?

Is it correct that when you use a state-reset-appserver, you only need to do this once (in the connection.p) ?

If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username, password and domain-name (tenant)).

Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the help, but what is clientcontextid ?

Kind regards

Bart S.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by bart.syryn on 24-Feb-2014 08:37

Thanks for all the responses.

Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at that point I can lookup the client-principal in activate.p.

With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-NAME = "tenantname". Is this correct ?

In a stateless-appserver, I need to read this information everytime there's a call to the appserver (activate.p) ?

Is it correct that when you use a state-reset-appserver, you only need to do this once (in the connection.p) ?

If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username, password and domain-name (tenant)).

Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the help, but what is clientcontextid ?

Kind regards

Bart S.

Posted by Peter Judge on 21-Feb-2014 07:58

I presented a Basics of Identity Management session at Exchange (and some PUGs) last year, which covers the flow of using the client-principal for authentication. The slides may be helpful, but there is also a working sample available, which includes an example of how to use C-P with WebSpeed.

The slides are at  www.progress.com/.../Track%202%20-%20Basics%20of%20Identity%20Management%20OE.pdf

The example code is at github.com/.../IdM_Sample .

If you have any questions about either the slides or code, please ask.

HTH,

-- peter

Posted by James Palmer on 21-Feb-2014 08:15

We use our own user table and I've just done a proof of concept to show that we can use the C-P so that we can start using OE Auditing. The C-P part took me a couple of hours to implement once I'd finished my reading around it.

Posted by bart.syryn on 21-Feb-2014 08:56

Hi James,

Can you share a sample on how you implemented it ? That can help a lot. Do you use stateless-appserver ?

Kind regards,

Bart Syryn

Posted by James Palmer on 21-Feb-2014 09:03

I'll see what I can do... It's just a proof of concept so still needs some work, but it's a start. 

DEFINE VARIABLE hClientPrincipal AS HANDLE    NO-UNDO.

/*Do user validation against our User table then...*/
RUN CreateCPObject(User_Name). PROCEDURE CreateCPObject : /*------------------------------------------------------------------------------ Purpose: Notes: ------------------------------------------------------------------------------*/ DEFINE INPUT PARAMETER ip-UserID AS CHARACTER NO-UNDO. CREATE CLIENT-PRINCIPAL hClientPrincipal. /*RUN Test.*/ hClientPrincipal:SESSION-ID = BASE64-ENCODE(GENERATE-UUID). hClientPrincipal:USER-ID = ip-UserID. hClientPrincipal:DOMAIN-NAME = 'TestDomain'. hClientPrincipal:DOMAIN-TYPE = 'Internal'. hClientPrincipal:LOGIN-EXPIRATION-TIMESTAMP = ADD-INTERVAL(NOW, 60000, 'seconds'). /*hClientPrincipal:ROLES = pcRoles.*/ hClientPrincipal:SET-PROPERTY('UserPlant', 'Norcross'). FIND person NO-LOCK WHERE person.name EQ FILL-IN_Name NO-ERROR. IF NOT AVAILABLE person THEN DO: hClientPrincipal:AUTHENTICATION-FAILED ('UserName Password authenitication failed.'). MESSAGE 'UserName Password authenitication failed.' VIEW-AS ALERT-BOX ERROR. RETURN. END. IF person.password NE ENCODE(FILL-IN_Password) THEN DO: hClientPrincipal:AUTHENTICATION-FAILED ('UserName Password authenitication failed.'). MESSAGE 'UserName Password authenitication failed.' VIEW-AS ALERT-BOX ERROR. RETURN. END. hClientPrincipal:SEAL("1234"). SET-DB-CLIENT(hClientPrincipal). /*RUN Test.*/ END PROCEDURE.


Posted by bart.syryn on 21-Feb-2014 08:54

We're planning to use 11.3.2 to set this up.  So I suppose that Client-context-id is available.

But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter ?  I don't see how we could pass it and check it.

We use stateless, so when the user logs in, we make a connection to the appserver.  The connection isn't closed, only when the users logs out of the application.  Every time there's a request to the appserver, we check at the client side if the handle-appserver is still valid, if valid just run the .p otherwise make a new connection.  So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great influence on performance ?

Posted by Peter Judge on 21-Feb-2014 09:24

>But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter
> ?  I don't see how we could pass it and check it.

In the activate.p, you can get SESSION:CURRENT-REQUEST-INFO:ClientContextId . In https://github.com/nwahmaet/IdM_Sample/blob/master/idm-appserver-bl/services/BusinessLogic/Activate.p ,from the sample I mentioned earlier, I do:

run Security/GetClientPrincipal.p on hSecurityTokenService (
    session:current-request-info:ClientContextId, output cToken).
 
rClientPrincipal = base64-decode(cToken).
 
create client-principal hClientPrincipal.
hClientPrincipal:import-principal(rClientPrincipal).
/* now user is set to requesting user */
security-policy:set-client(hClientPrincipal).
 

GetClientPrincipal.p above simply retrieves a raw-encoded version of the C-P from a database table, and returns it to the caller in base64 encoding.

> So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great
> influence on performance ?

I would think there's some impact, because there's some work being done. However, there are also costs incurred by not doing this work.

 

-- peter

 

 
 
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Friday, 21 February, 2014 09:56
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

We're planning to use 11.3.2 to set this up.  So I suppose that Client-context-id is available.

But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter ?  I don't see how we could pass it and check it.

We use stateless, so when the user logs in, we make a connection to the appserver.  The connection isn't closed, only when the users logs out of the application.  Every time there's a request to the appserver, we check at the client side if the handle-appserver is still valid, if valid just run the .p otherwise make a new connection.  So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great influence on performance ?

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Peter Judge on 21-Feb-2014 08:08

> But maybe it's better to do it the right way and work with CP.

> We have our own table of users, so we don't use _user.

It is absolutely not necessary to use _user when using C-P.

Changes made in 11.1 have made it very easy to use your own authentication realm (aka your user table), and it has been possible to do so all along (since the introduction of the C-P in 10.1A).

-- peter

 
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Friday, 21 February, 2014 07:30
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

Thanks for the replies.

I see that the set-effective-tenant is the easiest way.  So in every .p on the appserver side, we would need to set the tenant, and that can be easily done.

But maybe it's better to do it the right way and work with CP.

We have our own table of users, so we don't use _user.

At this moment, when the appserver .p is called, some parameters go along (one is the userid).

But I don't understand working with the CP.  Correct me if I'm wrong.

So the user logs in and the first call to the appserver is made. In our login.p (on the appserver), we check if it's a know user and also check password.  If I understand it correct, at that point we should make the CP and store it in a DB or something else together with the session:context-id.

When the user is logged in, than we need to pass on every call to the appserver (.p) the session:context-id.  Lookup the ID and with the associated CP do SET-DB-CLIENT(CP).

I could also use the 'activate' possibility in the openedge management, so that's the .p that is run on every call to the appserver before the .p that actually gets the data, but how do I pass the session:context-id to that .p ?

Another question, if you use CP, than you need to set SET-DB-CLIENT in every call to the appserver (stateless), what's the difference at that point with SET-EFFECTIVE-TENANT ?

Kind regards,

Bart Syryn

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by James Palmer on 21-Feb-2014 09:04

I'll add to that that you need to do some admin on the database too to set your domain up etc.

All Replies

Posted by Bill Wood on 21-Feb-2014 05:13

There are basically two approaches, depending on where you are enforcing security and the need to have a Single-Sign -On solution.

The 'easiest' way to simply assert tenancy when you need it.   The application must be working as a SUPER-TENANT, but if that is the case you can use the SET-EFFECTIVE-TENANT("tenantname") when necessary.  (or use the TENANT-WHERE clause).  

For WebSpeed this might be sufficient.  The WebSpeed agents would be super-tenants, and you would at the start of every http request figure out the relevant tenant and set the effective tenancy.

If you want to have a full Single-Sign-On between an ABL client and an AppServer, you need to basically have the SET-DB-CLIENT(handleToCP) when you start processing a request.   In a Stateless AppServer, you would need to have a mechanism to persist the client-principal once you have created it.

One typcial way to do this is to have a sessionID that lets you recover a previously created and sericalized Client-Principal (C-P) object.

- At some point (e.g. Connection.p) create and seal the C-P object, and export it to a safe store (file or db) under the session:context-Id.

- on each appserver call (activate.p), use the session:context-id to find and import the C-P.

- use SET-DB-CLIENT(hCP) to assert tenancy.

There are some options.   I found a PUG Challenge presentation by BravePoint that I may help explain this:

pugchallenge.org/.../CPObjectPUG1.pdf

Posted by Michael Jacobs on 21-Feb-2014 05:47

I would like to expand on Bill's response and offer another viewpoint:

I would not recommend the SET-EFFECTIVE-TENANT or TENANT-WHERE use except in the case of a maintenance utility that is used to coordinate data between multiple tenants, which is what they were made for.  These methods are simply unsafe because you lose the tenant isolation the database gives you, it bypasses any internal database auditing, it will not work well with table/field access permissions, and you have to be careful of audits (who generally do not like a client running code in the context of a privileged user who can access any tenant's data).   Bill is correct that if you do not need secure multi-tenant support the SET-EFFECTIVE-TENANT could make your job easier.   If you want/need a secure multi-tenant environment, the Client-Principal is more work but a better choice.

The Client-Principal is simply a container (i.e token) of authenticated user information that the OE database connection can validate and subsequently use to coordinate setting the user-id, tenant-id, auditing user-id (and privileges), and database permissions in a single operation.   It is relatively easy to generate, but the challenge lies in which application architecture use case you apply it to, because it can be used in a variety of them.  

You say your user logs into a stateless application, but where do you pick up the correct user-id for each request the client executes?   Generally your application login code would generate the C-P and stores it in the some location where it can be easily referenced on subsequent requests.  Your application probably already records the user-id for subsequent requests.  Once a request starts you retrieve who the user is, their C-P, and use SET-DB-CLIENT(hCP).   How the stateless request knows which authenticated user it is (and therefore their C-P) can be done in a variety of ways, but the starting point is to look into leveraging what your application already does.

The Client-Principal is also a means of passing user identity from one OpenEdge component (such as an AppServer to AppServer) in the 11.x releases so that you use the same user identity consistently in multiple places.  Just in case this is in your future.

I hope this helps you decide which is the best path to take for your application.  

Posted by wmtwood on 21-Feb-2014 06:05

WRT:

I would not recommend the SET-EFFECTIVE-TENANT or TENANT-WHERE use except in the case of a maintenance utility that is used to coordinate data between multiple tenants

I did say that using SET-EFFECTIVE-TENANT was the 'easiest' thing, but I agree with Mike that it does mean you are:

  • bypassing user authentication (and therefore per-user auditing)
  • not following 'best practices' for security

Posted by bart.syryn on 21-Feb-2014 06:29

Thanks for the replies.

I see that the set-effective-tenant is the easiest way.  So in every .p on the appserver side, we would need to set the tenant, and that can be easily done.

But maybe it's better to do it the right way and work with CP.

We have our own table of users, so we don't use _user.

At this moment, when the appserver .p is called, some parameters go along (one is the userid).

But I don't understand working with the CP.  Correct me if I'm wrong.

So the user logs in and the first call to the appserver is made. In our login.p (on the appserver), we check if it's a know user and also check password.  If I understand it correct, at that point we should make the CP and store it in a DB or something else together with the session:context-id.

When the user is logged in, than we need to pass on every call to the appserver (.p) the session:context-id.  Lookup the ID and with the associated CP do SET-DB-CLIENT(CP).

I could also use the 'activate' possibility in the openedge management, so that's the .p that is run on every call to the appserver before the .p that actually gets the data, but how do I pass the session:context-id to that .p ?

Another question, if you use CP, than you need to set SET-DB-CLIENT in every call to the appserver (stateless), what's the difference at that point with SET-EFFECTIVE-TENANT ?

Kind regards,

Bart Syryn

Posted by Michael Jacobs on 21-Feb-2014 06:53

The Client-Principal technology can work with your own built-in user account tables with no problems.  In fact, that was its first use case.

Your login scenario is correct.  The choice of storage location can vary, and which one should be chosen base on your application's architecture.

If you are writing stateful applications, especially in a web environment, there is always something that does back and forth between the client and the server.  Because you are doing a login, I can assume the application architecture is stateful.   So yes, the login operation would return to the client the session-id and pass it to the server on subsequent requests.   The same process as is used in most web applications.

When the request is received from the client you do a one-time operation: the session-id passed by the client is used as a key into the C-P storage and the C-P retrieved, followed by calling SET-DB-CLIENT() which will then tell you if the C-P is valid, expired, and do all the other internal settings.  After SET-DB-CLIENT() successfully executed, all of your ABL code paths then execute in the context of that tenant without passing additional user information or code changes for new tenants.

The user logout, or session expiration then removes the C-P from your storage location.

The activate procedure is a good place to change user identities because it is a single location and satisfies the best practice that all security route through a single point before application code executes.  In the later 11.x releases there is a 'client-context-id' that is generated and exchanged between OpenEdge clients and the AppServers, which is there to support server-side caching of Client-Principals.  You use it like you would with a web browser and passing the http session-id back and forth.  The later 11.x releases also allow you to pass the Client-Principal itself between OpenEdge clients and the AppServer - which supports the client-side caching of Client-Principals.   Kind of like a web browser passing back and forth a cookie with the user's information in it.  The 'client-context-id' is generally kept in sync with the C-P:session-id, which makes it easier to adapt to different clients.

Yes, you call SET-EFFECTIVE-TENANT or SET-DB-CLIENT() one time at the start of each client request.   The difference: SET-EFFECTIVE-TENANT provides no security and you have to do all of the user to tenant lookup yourself; SET-DB-CLIENT() is secure and does all of the user-id to tenant resolution for you.

Again, your choice based on what requirements your application must meet.

Posted by Peter Judge on 21-Feb-2014 07:58

I presented a Basics of Identity Management session at Exchange (and some PUGs) last year, which covers the flow of using the client-principal for authentication. The slides may be helpful, but there is also a working sample available, which includes an example of how to use C-P with WebSpeed.

The slides are at  www.progress.com/.../Track%202%20-%20Basics%20of%20Identity%20Management%20OE.pdf

The example code is at github.com/.../IdM_Sample .

If you have any questions about either the slides or code, please ask.

HTH,

-- peter

Posted by Peter Judge on 21-Feb-2014 08:08

> But maybe it's better to do it the right way and work with CP.

> We have our own table of users, so we don't use _user.

It is absolutely not necessary to use _user when using C-P.

Changes made in 11.1 have made it very easy to use your own authentication realm (aka your user table), and it has been possible to do so all along (since the introduction of the C-P in 10.1A).

-- peter

 
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Friday, 21 February, 2014 07:30
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

Thanks for the replies.

I see that the set-effective-tenant is the easiest way.  So in every .p on the appserver side, we would need to set the tenant, and that can be easily done.

But maybe it's better to do it the right way and work with CP.

We have our own table of users, so we don't use _user.

At this moment, when the appserver .p is called, some parameters go along (one is the userid).

But I don't understand working with the CP.  Correct me if I'm wrong.

So the user logs in and the first call to the appserver is made. In our login.p (on the appserver), we check if it's a know user and also check password.  If I understand it correct, at that point we should make the CP and store it in a DB or something else together with the session:context-id.

When the user is logged in, than we need to pass on every call to the appserver (.p) the session:context-id.  Lookup the ID and with the associated CP do SET-DB-CLIENT(CP).

I could also use the 'activate' possibility in the openedge management, so that's the .p that is run on every call to the appserver before the .p that actually gets the data, but how do I pass the session:context-id to that .p ?

Another question, if you use CP, than you need to set SET-DB-CLIENT in every call to the appserver (stateless), what's the difference at that point with SET-EFFECTIVE-TENANT ?

Kind regards,

Bart Syryn

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by James Palmer on 21-Feb-2014 08:15

We use our own user table and I've just done a proof of concept to show that we can use the C-P so that we can start using OE Auditing. The C-P part took me a couple of hours to implement once I'd finished my reading around it.

Posted by bart.syryn on 21-Feb-2014 08:54

We're planning to use 11.3.2 to set this up.  So I suppose that Client-context-id is available.

But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter ?  I don't see how we could pass it and check it.

We use stateless, so when the user logs in, we make a connection to the appserver.  The connection isn't closed, only when the users logs out of the application.  Every time there's a request to the appserver, we check at the client side if the handle-appserver is still valid, if valid just run the .p otherwise make a new connection.  So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great influence on performance ?

Posted by bart.syryn on 21-Feb-2014 08:56

Hi James,

Can you share a sample on how you implemented it ? That can help a lot. Do you use stateless-appserver ?

Kind regards,

Bart Syryn

Posted by James Palmer on 21-Feb-2014 09:03

I'll see what I can do... It's just a proof of concept so still needs some work, but it's a start. 

DEFINE VARIABLE hClientPrincipal AS HANDLE    NO-UNDO.

/*Do user validation against our User table then...*/
RUN CreateCPObject(User_Name). PROCEDURE CreateCPObject : /*------------------------------------------------------------------------------ Purpose: Notes: ------------------------------------------------------------------------------*/ DEFINE INPUT PARAMETER ip-UserID AS CHARACTER NO-UNDO. CREATE CLIENT-PRINCIPAL hClientPrincipal. /*RUN Test.*/ hClientPrincipal:SESSION-ID = BASE64-ENCODE(GENERATE-UUID). hClientPrincipal:USER-ID = ip-UserID. hClientPrincipal:DOMAIN-NAME = 'TestDomain'. hClientPrincipal:DOMAIN-TYPE = 'Internal'. hClientPrincipal:LOGIN-EXPIRATION-TIMESTAMP = ADD-INTERVAL(NOW, 60000, 'seconds'). /*hClientPrincipal:ROLES = pcRoles.*/ hClientPrincipal:SET-PROPERTY('UserPlant', 'Norcross'). FIND person NO-LOCK WHERE person.name EQ FILL-IN_Name NO-ERROR. IF NOT AVAILABLE person THEN DO: hClientPrincipal:AUTHENTICATION-FAILED ('UserName Password authenitication failed.'). MESSAGE 'UserName Password authenitication failed.' VIEW-AS ALERT-BOX ERROR. RETURN. END. IF person.password NE ENCODE(FILL-IN_Password) THEN DO: hClientPrincipal:AUTHENTICATION-FAILED ('UserName Password authenitication failed.'). MESSAGE 'UserName Password authenitication failed.' VIEW-AS ALERT-BOX ERROR. RETURN. END. hClientPrincipal:SEAL("1234"). SET-DB-CLIENT(hClientPrincipal). /*RUN Test.*/ END PROCEDURE.


Posted by James Palmer on 21-Feb-2014 09:04

I'll add to that that you need to do some admin on the database too to set your domain up etc.

Posted by Peter Judge on 21-Feb-2014 09:24

>But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter
> ?  I don't see how we could pass it and check it.

In the activate.p, you can get SESSION:CURRENT-REQUEST-INFO:ClientContextId . In https://github.com/nwahmaet/IdM_Sample/blob/master/idm-appserver-bl/services/BusinessLogic/Activate.p ,from the sample I mentioned earlier, I do:

run Security/GetClientPrincipal.p on hSecurityTokenService (
    session:current-request-info:ClientContextId, output cToken).
 
rClientPrincipal = base64-decode(cToken).
 
create client-principal hClientPrincipal.
hClientPrincipal:import-principal(rClientPrincipal).
/* now user is set to requesting user */
security-policy:set-client(hClientPrincipal).
 

GetClientPrincipal.p above simply retrieves a raw-encoded version of the C-P from a database table, and returns it to the caller in base64 encoding.

> So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great
> influence on performance ?

I would think there's some impact, because there's some work being done. However, there are also costs incurred by not doing this work.

 

-- peter

 

 
 
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Friday, 21 February, 2014 09:56
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

We're planning to use 11.3.2 to set this up.  So I suppose that Client-context-id is available.

But if you would use the session-id on the client side to pass that to the appserver. How can the activate.p on the appserver recieve that parameter ?  I don't see how we could pass it and check it.

We use stateless, so when the user logs in, we make a connection to the appserver.  The connection isn't closed, only when the users logs out of the application.  Every time there's a request to the appserver, we check at the client side if the handle-appserver is still valid, if valid just run the .p otherwise make a new connection.  So everytime there's a request on the appserver the activate.p would run, and checks the session-id and CP to set the client.  Has that a great influence on performance ?

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by bart.syryn on 24-Feb-2014 08:37

Thanks for all the responses.

Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at that point I can lookup the client-principal in activate.p.

With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-NAME = "tenantname". Is this correct ?

In a stateless-appserver, I need to read this information everytime there's a call to the appserver (activate.p) ?

Is it correct that when you use a state-reset-appserver, you only need to do this once (in the connection.p) ?

If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username, password and domain-name (tenant)).

Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the help, but what is clientcontextid ?

Kind regards

Bart S.

Posted by Peter Judge on 24-Feb-2014 11:06

> Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at
> that point I can lookup the client-principal in activate.p.

Yes, exactly.

> With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-
> NAME = "tenantname". Is this correct ?

Yes (DOMAIN-NAME = 'domain' not 'Tenant' but close enough). Domain :: Tenant is a many-to-one relation.

>If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username,
>password and domain-name (tenant)).

Don't need to pass any extra handles for authentication? No. Note that you can – as an alternative – pass the actuall CLIENT-PRINCIPAL instead of the ClientContextID (see the doc below for more info).

>Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the
> help, but what is clientcontextid ?

Yes, the property is available in the ABL. Note that it is only available on the SESSION handle on the AppServer. On the client, you get at it from the server hander (ie hAppServer).

It's a property on the Progress.Lang.OERequestInfo object, which is what the CURRENT-REQUEST-INFO attribute holds. http://documentation.progress.com/output/OpenEdge113/oe113html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=ABL/ABL%20Reference/23dvref-Classes.037.41.html for moree

I do not know what the implications are for state aware AppServer modesl (-reset and friends).
 
-- peter
 
[collapse]
From: bart.syryn [mailto:bounce-bartsyryn@community.progress.com]
Sent: Monday, 24 February, 2014 09:38
To: TU.OE.Development@community.progress.com
Subject: RE: Multi-tentant - appserver - webspeed
 
Reply by bart.syryn

Thanks for all the responses.

Correct me if I'm wrong.  But with the 'SESSION:CURRENT-REQUEST-INFO:ClientContextId' i know at the appserver-side the session-id of the client.  So at that point I can lookup the client-principal in activate.p.

With SET-DB-CLIENT(client-principal:handle), i can set the database and also the tenant ? So at login I need to set the correct tenant : client-principal:DOMAIN-NAME = "tenantname". Is this correct ?

In a stateless-appserver, I need to read this information everytime there's a call to the appserver (activate.p) ?

Is it correct that when you use a state-reset-appserver, you only need to do this once (in the connection.p) ?

If you use the session:current-request-info:clientcontextid you don't need to pass any extra handles etc. to the appserver (except at login : the username, password and domain-name (tenant)).

Is the session:current-request-info:clientcontextid available in ABL ?  We still use the appbuilder.  I find information about the session:current-request-info in the help, but what is clientcontextid ?

Kind regards

Bart S.

Stop receiving emails on this subject.

Flag this post as spam/abuse.

[/collapse]

Posted by Michael Jacobs on 24-Feb-2014 12:25

Adding to Peter's information:  

The implications are that for state-reset and state-aware connection models the user-id (Client-Principal) only needs to be set once at connect time in the AppServer.   For the stateless and State-free connection models the user-id (Client-Principal) needs to be set on every request because each request will run in a different Agent process.

This thread is closed