HowTo: Mapping Application Users to GAM - Adding a secondary attribute referencing the GAMUser

Official Content

In this scenario, a secondary attribute to the application Users table, named "UserIdentification" of GAMGUID data type will be added, which links the users to the GeneXus Access Manager database (Figure 2). Note that there are other ways to identify the GAM user other than using GAMGUID.

MapAppUserstoGAMAlterA

Figure 1.

MapAppUserstoGAMAlterA2

Figure 2.

The UserIdentification attribute can be set as Candidate Key depending on the needs of the application.

In this solution, the users are redundant in GAM database table and in the application database "AppUsers" table, so there has to exist a mechanism to keep both tables updated. Here we show a way to do it.

Keep GAM User table and the application AppUsers table updated

1. In order to insert the Users of AppUsers table in GAM database, you need to run a Procedure that scans the "AppUsers" table and updates the information in GAM database table (inserts the users in GAM Repository).

The Procedure code would be similar to the following:

For each UserCod

    &User.GUID = &UserId  //&User is GAMUser data type, &UserId is GAMGUID data type
    &User.Name            = UserName
    &User.FirstName        = UserFirstName
    &User.LastName        = UserLastName
    &User.Password        = UserPassword
    &User.EMail = UserEmail
    &User.Save()

    if &User.Success()
        msg('User added to GAM Repository: ' + &User.Name)
    else
        &GAMErrors = &User.GetErrors() //&GAMErrors is collection of GAMError data type
        do 'ProcessErrors'
    endif

Endfor
commit
Sub 'ProcessErrors'
    For &GAMError in &GAMErrors
        Msg(Format("%1 (GAM%2)", &GAMError.Message, &GAMError.Code))    
    EndFor
EndSub

2. Then, in the "Registration form" of the application, you need to update both, the user in the application database, and the same user in GAM database. So, for each user who is registered (or updated, or deleted in the application) we need to update GAM repository with those changes.

The following are the rules of the transaction "AppUsers" which is going to be used to manage users in this sample:

[web]
{
parm(in:&Mode, in:&UserCod);

UserCod = &UserCod if not &UserCod.IsEmpty();
noaccept(UserCod);
noprompt(UserCod);
}

UpdateGAMUser(UserIdentification,UserName,UserFirstName,UserLastName,UserAddress,UserPhone,UserEmail,&Error)
    if update
    on aftervalidate;

DeleteGAMUser(UserIdentification,&Error) if delete  on aftervalidate;

error('Please enter your Email') if UserEmail.IsEmpty();

[web]
{
    accept(&Password);
    accept(&PasswordConfirm);
   
    error('Passwords don´t match')
        if insert and &Password <> &PasswordConfirm;
       
    RegisterGAMUser(UserName,UserFirstName,UserLastName,UserAddress,UserPhone,UserEmail,&Password,UserIdentification,&Error)
            if insert  on aftervalidate;

}
    error('Data could not be updated, try again please') if &Error on aftervalidate;

Notes

The Procedures "RegisterGAMUser", "UpdateGAMUser", and "DeleteGAMUser" have Commit on Exit = No. The idea is to simulate only one LUW, so that the user is updated in both databases (app DB, and GAM DB) or none of them.
That is why the &Error flag is used also, which is returned by those Procedures if there is a failure in GAM database user update.

Download the sample for more details.

Take into account that if the GAM backend is going to be used to manage users also, the web panels of the GAM backend have to be modified also so that data is updated in "Users" table of the application database.

The following is a sample code of "RegisterGAMUser" Procedure, to take as an example:

&User.Name = &UserName   //&User is GAMUser data type
&User.FirstName = &UserFirstName
&User.LastName = &UserLastName
&User.Address = &UserAddress
&User.Phone = &UserPhone
&User.EMail = &UserEmail
&User.Password = &UserPassword
&User.Save()

if &User.Success()
        &UserIdentification = &User.GUID //&UserIdentification is GAMGUID data type
        &Error = False
else
    &GAMErrors = &User.GetErrors()  //&GAMErrors is collection of GAMError data type
    do 'ProcessErrors'
    &Error = True
endif

Sub 'ProcessErrors'
    For &GAMError in &GAMErrors
        Msg(Format("%1 (GAM%2)", &GAMError.Message, &GAMError.Code))
       
    EndFor
EndSub

parm(in:&UserName,in:&UserFirstName,in:&UserLastName,in:&UserAddress,in:&UserPhone,in:&UserEmail,in:&UserPassword,out:&UserIdentification,out:&Error);

NOTE

 For GeneXus 15, GAM Events subscription allows triggering a Procedure automatically when the GAM user is updated using GAM.

Example. Filtering data of a user 

Suppose that a user has to see only the "TourReservations" corresponding to him.

In order to filter only the data related to the user who has logged in, you need to add the following condition to the WWTourReservation Web Panel:

UserCod = GetUser();

wwtourReserv

Figure 3.

Where GetUser is a Procedure that gets the GAM user who is logged in and returns the corresponding UserCod of the application database:

&User = GAMUser.Get()
&GAMErrors = &User.GetErrors()
if &GAMErrors.Count > 0
    do 'ProcessErrors'
else
    &UserIdentification = &User.GetId()
     for each UserCod
         where UserIdentification = &UserIdentification
         &UserCod = UserCod
    endfor
endif

Sub 'ProcessErrors'
For &GAMError in &GAMErrors
    Msg(Format("%1 (GAM%2)", &GAMError.Message, &GAMError.Code))
EndFor
endsub

parm(out:&UserCod);

The same when calling the Transaction for registering new TourReservations, you have the following rule, so as the UserCod is instantiated with the user who has logged in:

equal(UserCod, GetUser());

The code of getting the user can read the user from a web session also.

Download the sample xpz from here.

See Also

HowTo: Mapping Application Users to GAM Users