1

For the impatient - I can summarize this question as:

What practical approach can be used to leverage role-based privileges in PostgreSQL when using an Access Front End that employs ODBC linked-tables?

And now for the longer version:

I've inherited the unsavory task of upgrading an Access 2000 / PG 7 application to Access 2013 / PG 9. I'm new to PostgreSQL but have used Oracle and Microsoft Access quite a bit.
EDIT: The production server is running PostgreSQL on Mac OS X Lion. My Test machine is running PostgreSQL on Oracle Linux 7.

This Access DB is linking to tables in the PG Database via ODBC, connecting using an single PG login role (application_user). Every user connects with this login role, and it is only the conditions in the Forms / VBA that limits the user's rights. If, however, a user can get into the navigation pane - they can access the linked tables directly and bypass all security restrictions. While upgrading this database, I'd like to see if I can tighten that up.

I could set up each user with their own login role on PostgreSQL, but then it would mean (from the way I'm looking at it) a hefty amount of retooling the database. I'd rather not make such large changes on a production database - incremental changes are more desired.

Looking at the database's security needs - I can think of only five roles that would be needed.

  • Order Entry
  • Customer Entry
  • Order and Customer Entry
  • Read-Only
  • Not Authorized - No Access

I can set up these as Group Roles in PGSQL and each table with the necessary ACL for each role.

What I'm missing is how I can go from a single login-role (application_user) to all of the above roles?

My initial thought was to set the application_user (logon role) to have no group roles (essentially resulting in "Not Authorized - No Access"), and then use a call to a PL/pgSQL function authorize(Username, MD5PassWord) to authorize and elevate the role. The function would check if the supplied MD5 hash matches the MD5 hash stored in the users table - and if so - it would issue a SET SESSION ROLE for the appropriate Group Role.
If this would work, it would let me track user names that are logging in, and then using the pg_backend_pid() function, I can associate it back with the user for the business logic or logging or whatever. It also means I don't need to worry if some user goes into the Linked Table - because their access would be restricted by whatever role they are currently authorized for in that database session.

So I whipped up a plpgsql script, set its owner to OrderCustomerEntryGroup and gave it SECURITY DEFINER rights.

DECLARE
    v_Status integer;
BEGIN
    v_Status := 0;
    IF pin_username = 'username' AND MD5('foo') = pin_pwmd5 THEN
      SET SESSION AUTHORIZATION OrderEntryGroup;
      v_Status := 1;
    END IF;
    RETURN v_Status;
END;

Only problem however with my implementation is that

SELECT authenticate('username',MD5('foo'));

gives:

ERROR: cannot set parameter "session_authorization" within security-definer function
SQL state: 42501
Context: SQL statement "SET SESSION AUTHORIZATION OrderEntryGroup"
PL/pgSQL function authenticate(character varying,text) line 7 at SQL statement

So I read up on this - and from what I can tell, you used to be able to do this, but for whatever reason it was removed. I haven't been able to find an alternative - other than using the built in roles on a per-user level.

So what I'm asking is .. What am I missing to make my approach (an easy solution) work, or is there a better way of doing this that won't involve ripping apart the existing access database?

1 Answer 1

3

If you want to restrict access to the database from a direct connection then you'll need to do a certain amount of "retooling" on the back-end regardless. The best approach is almost always to have each user connect with their own credentials and then restrict what that user can do based on the groups (sometimes referred to as "roles") to which they belong in the database.

If you want to avoid having to set up separate database userids/passwords for each network user then you should investigate using integrated Windows authentication (SSPI) as discussed in another question here. You'll still need to define the users (in addition to the groups/roles) at the database level, but you'd have to do most of that work anyway.

Sign up to request clarification or add additional context in comments.

5 Comments

Admittedly I am not familiar with SSPI, but I should have pointed out that the production Database is running on Mac OS X Lion (not server) and my test database is running on Oracle Linux 7. I looked at PostgreSQL's authentication modules and decided that I'd rather not introduce another layer for me to support - and rather stick with username / passwords. Setting up each user on the Database Server is not that much of an issue. How would one go about dynamically changing the credentials in the access database in a way that wouldn't leave them saved in the connection string?
@DHW I just tested with PostgreSQL ODBC v9.x and it seems to cache credentials like SQL Server does (and MySQL, IIRC, doesn't). In that case your users would be able to enter their PostgreSQL username and password once when your Access application first tries to open a PostgreSQL linked table, and thereafter it would continue to use the cached credentials for as long as your Access application is open. Do some testing with that approach (since my PostgreSQL server was on Windows, not Linux/Mac), and if it works for you then you won't have to save credentials in the connection strings.
To NOT store logon in the connection string, so you don't have to re-link with different users, see this tip: blogs.office.com/b/microsoft-access/archive/2011/04/08/…
Albert / Gord: This approach sounds promising. I've used DSN-less connection strings before, but wasn't aware of the caching advantage. Its fairly simply to trap for ODBC errors (DAO.Errors) and if it looks like the cached connection, for whatever reason, has disappeared - to simply pop up a form asking them to log in again. I'm going to play around with it a bit more and I'll let you know how it works. Thanks
Just to let you know - I got this working. I haven't had a lot of time to devote to this project - but I did make a test database just to try out the DSN-less connections, and I was able to get it working. Going to make your answer as accepted. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.