The point of proxy authentication is that
- The proxy user only has "create session" privileges but can't do much else.
- The real user does not have "create session" privileges and cannot log on to the database without knowing the proxy user name and password (and that is only set on the web/application server).
- The USER function returns the real user name, and all standard database auditing, roles, etc. work as usual.
As of version 1.3 of the Thoth Gateway, proxy authentication is now supported. Here is how it works:
IIS Setup
Set up the application (virtual directory) in IIS where the gateway runs with Integrated Windows Authentication, so that the CGI environment variable LOGON_USER will be populated with the client's Windows username. (If the user is using Internet Explorer to browse the site, his identity will be passed on to the web server/gateway automatically; if using another browser, then an explicit logon is required.)
Oracle Setup
Define an "application server user", ie the common user that connections will be established through:
-- Log on as DBA (SYS or SYSTEM) that has CREATE USER privilege.
create user appserver identified by eagle;
create user end_user identified by secret;
grant create session to end_user;
alter user end_user grant connect through appserver;
Now test the setup with SQL*Plus, by connecting with the "application server user", and then "becoming" the end user:
-- note we don't specify the end_user password, but still become that user
SQL> connect appserver[end_user]/eagle
Connected.
SQL> select user from dual;
USER
------------------------------
END_USER
SQL>
Note that since the point of this is to take advantage of existing Active Directory accounts, you probably want to create your users like this:
create user "your_domain\end_user" identified externally;
grant create session to "your_domain\end_user";
alter user "your_domain\end_user" grant connect through appserver;
Thoth Gateway Setup
In web.config, modify the DAD settings (the following example assumes a local Oracle XE installation):
param name="DatabaseConnectString" value="//127.0.0.1:1521/xe"
param name="DatabaseConnectStringAttributes" value="Enlist=false;Proxy User Id=appserver;Proxy Password=eagle;"
param name="DatabaseUserName" value="LOGON_USER"
param name="DatabasePassword" value=""
Notice the value "LOGON_USER" specified for the DatabaseUserName parameter. This is a reserved string that will be replaced with the actual value of the LOGON_USER value from the web request (ie. the user's Windows username, typically "domain\username"). You can also specify "LOGON_USER_NO_DOMAIN" to strip away the domain part of the user name -- what you use will depend on how you have set up your user accounts in Oracle.
Testing It
To test that everything works at this point, create a procedure similar to the following, and execute it via the gateway (don't forget to grant execute privileges on it to the end-user's account, and create a public synonym for it unless you prefix with the procedure owner's name in the URL).
procedure test_proxy_auth
as
begin
htp.header(1, 'Proxy authentication');
htp.ulistopen;
htp.listitem ('USER = ' || user);
htp.listitem ('Proxy user = ' || sys_context('userenv', 'proxy_user'));
htp.listitem ('CGI LOGON_USER = ' || owa_util.get_cgi_env('LOGON_USER'));
htp.ulistclose;
end test_proxy_auth;
If successful, the USER function should return the end-user's Windows username, and the Proxy User should display as "appserver".
Postscript: A little enigma
Actually, if you do as described above, you could possibly get this error when you try to run the procedure via the gateway:
ORA-1045: user %s lacks CREATE SESSION privilege; logon denied
At least, that's what I got . To get around it, I had to explicitly grant this to the "appserver" user:
grant create session to appserver;
The funny thing is that my example above, tested via SQL*Plus, shows that this works without the grant! But when attempting the same connection via ODP.NET, it gives the above error unless the grant is made.
And if I revoke the "create session" from the end_user, the above example doesn't work in SQL*Plus, because of the missing privilege. Which seems to contradict the purpose of proxying, as defined at the top of this blog post.
And if I revoke the "create session" from the end_user, the above example doesn't work in SQL*Plus, because of the missing privilege. Which seems to contradict the purpose of proxying, as defined at the top of this blog post.
If anybody knows why SQL*Plus and ODP.NET show different behaviour here, please let me know.