Question

I store permission data in database and session data in memory/filesystem. I store the processed form of permissions per user in session data. I want to refresh that permission cache after user permissions changed in the database. User permissions can change due to change of role permissions, user extra permissions and permission details. So there are a lot of stored procedures which can lead to user permission change. I can collect the list of that users, but how should I send it back? (I already use return values to send back records, etc...)

Answer for "I am not sure what you are asking exactly: How should I send it back?. Please clarify and add a code example (even if it's not working)."

I store the session data in memory or in filesystem, and I store user permissions in the database. The authorization logic is very complex, so by successful authentication I collect every permission the user has, parse it with a custom parser, and save the results into the session as a cache. After that I use that cache by permission check. The problem is that by permission change, role change, role permission change, role membership change, etc... I have to update that permission caches in the sessions.

A simplified version with a REST interface:

POST /sessions {email: "", password: ""}
 -> {token: "qwerty", user: {id: 123}}
 -> sessionStore.create("qwerty", {user: {id: 123}, permissions: "abcd"}) 
POST /roles {name: "administrator", permissions: "efgh"}
 -> {id: 567}
POST /roleMmembership {user: {id: 123}, role: {id: 567}}
 -> {id: 890}
 -> sessionStore.update("qwerty", {user: {id: 123}, permissions: "abcdefgh"}) 

As you see after I had added user 123 to role 567, the session was updated, and permissions "efgh" were added to the session permission cache. On database level the new membership calls a stored procedure which adds the membership, and returns the id. I can see in this procedure which user has permission change, but I cannot send back that information because a stored procedure can have only one return value. Sending that information with notify is a great idea because it should be more like an event than a return value. Sadly in my database driver the notify is not supported, and I don't want to change driver... So I need a workaround. It could be multiple return values, global variables, etc... I think using temporary tables is the only possible solution, but I don't have too much experience with postgres...

Was it helpful?

Solution

1.)

LISTEN and NOTIFY would be the best solution, but my driver (php pdo driver) sadly does not support that, and I think it never will, because this is a postgres specific feature.

2.)

I think with my driver the only option to use temporary tables. I created the following functions for that:

init

CREATE FUNCTION policy_change_provide_store ()
  RETURNS VOID
AS
  $BODY$
  BEGIN
    CREATE TEMPORARY TABLE policy_change (
      user_id INT4
    );
    EXCEPTION WHEN duplicate_table THEN
      RETURN;
  END;
$BODY$
LANGUAGE plpgsql VOLATILE;

update

CREATE FUNCTION policy_change_update (
  IN user_ids INT4 []
)
  RETURNS VOID
AS
  $BODY$
  BEGIN
    PERFORM policy_change_provide_store();
    INSERT INTO policy_change
    SELECT inserted_user_id FROM unnest(user_ids) AS inserted_user_id
    WHERE NOT EXISTS(SELECT 1 FROM policy_change WHERE user_id = inserted_user_id);
  END;
  $BODY$
LANGUAGE plpgsql VOLATILE;

read

CREATE FUNCTION user_read_all_by_policy_change ()
  RETURNS TABLE (
  user_id INT4
  )
AS
  $BODY$
  BEGIN
    PERFORM policy_change_provide_store();
    RETURN QUERY
    SELECT policy_change.user_id FROM policy_change ORDER BY policy_change.user_id DESC;
  END;
  $BODY$
LANGUAGE plpgsql VOLATILE;

So I update the policy change data from my postgres functions with policy_change_update(user_ids) and read that from the http server with user_read_all_by_policy_change(). Ye, this is ugly, but it works well. If you got a better solution please let me know!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top