#auth.requires_permission not working ver 2 - authentication

Good day to all web2py experts!
I can't find a way on how to use the web2py Decorators
#auth.requires_permission('read','person')
def f(): ....
in the pdf manual it says that:
prevents visitors from accessing the function f unless the visitor is a member
of a group whose members have permissions to "read" records of table
"person". If the visitor is not logged in, the visitor gets directed to a login
page (provided by default by web2py). web2py also supports components,
i.e. actions which can be loaded in a view and interact with the visitor via
Ajax without re-loading the entire page. This is done via a LOAD helper which
allows very modular design of applications; it is discussed in chapter 3 in the
context of the wiki and, in some detail, in the last chapter of this book.
This 5th edition of the book describes web2py 2.4.1 and later versions
In my case:
I have list of groups: Admin_Tier_1, Admin_Tier_2, Admin_Tier_3
Admin_Tier_1 - has the highest authority to access all features like adding a school year, set a school year etc.
Admin_Tier_2 - has the authority to add students etc
Admin_Tier_3 - its the lowest level of authority that can only add fines to the students (Organization Officers)
now I use the Decorator code like this:
#auth.requires_permission('Admin_Tier_1','student_list')
def add(): ....
now I login the account of the Chairman which registered in the auth_membership as Admin_Tier_1. Then I click the link "List of Students" which redirect to add(): function but the system returned a message:
Not Authorized
Insufficient privileges

The auth.requires() method can take a callable rather than a boolean value as the condition, and this is preferable when it is expensive to generate the boolean value (otherwise, the boolean value is generated whenever the controller is accessed, even if the particular decorated function is not the one being called). So, to avoid calling auth.has_membership unnecessarily, you can do:
#auth.requires(lambda: auth.has_membership('Admin_Tier_1') or
auth.has_membership('Admin_Tier_2'))
Now the two auth.has_membership calls will only be made when the actual function being decorated is called.
Also, if you need to check a larger number of roles, you can do something like:
#auth.requires(lambda: any([auth.has_membership(r) for r in ['list', 'of', 'roles']))

Problem solved:
#auth.requires(auth.has_membership('Admin_Tier_1') or auth.has_membership('Admin_Tier_2'))
source here.
Whenever I access the page if the user belong to the group of Admin_Tier_3 the system block the acess and redirect it to "/default/user/not_authorized" page :)

Related

Specifying multiple Domain Bases in Rocket.Chat LDAP

On Rocket.Chat's LDAP configuration page, the helper text for Domain Base states that you should enter (emphasis mine):
The fully qualified Distinguished Name (DN) of an LDAP subtree you want to search for users and groups. You can add as many as you like; however, each group must be defined in the same domain base as the users that belong to it. If you specify restricted user groups, only users that belong to those groups will be in scope. We recommend that you specify the top level of your LDAP directory tree as your domain base and use search filter to control access.
Problem is, I don't know how to enter more than one.
My DN looks like this:
OU=IT,OU=Staff,DC=companyname,DC=local
And I want the following users to also be synced:
OU=Example,OU=Staff,DC=companyname,DC=local
But I don't know how to add them both, as the docs aren't clear, and the source code is even less clear.
I've tried the following ways:
Space separated
Semicolon separated
Ampersand (and double ampersand) separated
Wrapping them up in an array (e.g. ["OU=Example ...", "OU=IT ..."]) and as a JSON object
Pipe (and double pipe) separated
'Plus' separated (e.g. DC=local + OU=Example)
But no matter what I do, it won't sync users. The logs tell me:
Exception while invoking method 'ldap_sync_users' NoSuchObjectError: 0000208D: NameErr: DSID-03100238, problem 2001 (NO_OBJECT), data 0, best match of: at Object.Future.wait (/snap/rocketchat-server/511/node_modules/fibers/future.js:449:15) ...
I know I can set up a group restriction so only users in a certain group will be synced, but the helper text says I can use multiple DNs, and I want to know how to use multiple DNs
After reading RFC-4514, I discovered I should construct my DN like so:
OU=Example+OU=IT,OU=Staff,DC=companyname,DC=local
With the plus occurring between the two OUs I wish to add. Now my users are syncing correctly.

How to prevent DataTables from displaying or hiding columns on the basis of an obsolete saved state

I have a table driven by DataTables 1.10. Filtering is turned on. When I talk about "doing a search" below, I'm talking about using the filtering function of this table.
Description of the Problem
Everything works fine with stateSave off. However, when stateSave is on, the following happens:
Alice logs in as admin. Because admin has all privileges, when she does a search through articles, she can see all articles. Because some articles are published and some are unpublished the table has a column that show which are published and which are not. So far so good.
Bob, a random user, accesses the site. Random users cannot ever see unpublished articles so the table hides the column that shows publication status. So far so good.
Alice logs out. She now accesses the site like a random user. So she should see exactly what Bob sees. However, when she does a search she still sees the column that indicates publication status.
(Note: The issue I'm discussing here is purely one of user interface. The server ensures that unprivileged users cannot ever get a record for an unpublished article. The problem though is that the additional column gives unpriviledged users information that they do not need. They can only see published articles in their search so they don't need to see that every article they get in a search is published.)
The code that configures the datatable hides the publication column by doing something like this:
var columnDefs = [];
if (!privileged) {
columnDefs.push({
targets: [1],
orderable: false,
visible: false
});
}
columnDefs is passed to DataTables as the columnDefs option.
Technical Reason for the Problem
The problem is that DataTables store things like column visibility into the state it saves into localStorage. So when Alice logs out and makes a search again as an unprivileged user, even though the value of columnDefs is correct, it is overwritten by the saved state. That state was stored when Alice was an admin, and it declared the publication column to be visible, so it remains visible even when Alice is accessing the site as an unprivileged user.
What I want is for users to benefit from the saved state but avoid having this state carry over when the user's privileges change.
Caveats:
I don't want to use sessionStorage because I want the state to persist between browser closings, but sessionStorage is cleared when the browser is closed.
I cannot use the session cookie assigned by the server to detect logins and logouts because it is HTTP only. Besides, privileges could change for other reasons.
I do not want to arbitrarily set an expiration time on the saved state.
The solution I've settled on is to use an additional field in the saved data to know when the conditions I care about have changed. This is a field whose value changes depending on the privileges that the user currently has. For instance, because in the case I described here, I decide to hide or show a column on the basis of a variable named priviledged (which is initialized from data provided by the server), it could be as simple as:
var token = privileged;
Then I set stateSaveParams to record the token when the state is saved:
stateSaveParams: function (settings, data) {
data.myapp_token = token;
}
The prefix myapp_ is just there to avoid possible collisions with DataTable's own fields.
I set stateLoadParams so that if the current value of token differs from what has been recorded before, the state is cleared:
stateLoadParams: function (settings, data) {
if (data.myapp_token !== token) {
this.api().state.clear(); // Clears the state.
return false; // Tells DataTables to not use the state that was stored.
}
// This return is here to keep the IDE happy but does not do anything special.
return undefined;
},
I've just set token to the single condition I've shown in my question (privileged) in this example but in production I use a combination of variables plus a local version number so I can bump the value of token as needed if I do something that requires clearing the state but cannot be detected just as a privilege change.

Rails cache_digests and conditionals

When we cache a partial in rails using cache digests, how does the conditional logic in the partial get handled? Does it cache the full template and later apply the conditionals so that the right json/html can be served to the right user?
Does it cache the full template and later apply the conditionals so
that the right json/html can be served to the right user?
This part of question seems a bit unclear to me, so I'll provide different options based on what "conditionals" could be.
First of all, cache digests do not care about inner conditions based on #variables's state (unless a particular state is mentioned inside of its cache key). Consider the following example:
# users.haml
.welcome_block
- if #user.admin?
%h4 Hello, admin!
- else
%h4 Hello, user!
In case you apply caching to the whole page with cache ['users_haml'], the cache would be generated just once (for the first user with whichever role). Any user who accessed this page later would see the same h4 greeting as the one which has been shown to the first user. The reason here is that digest for string users_haml, proved to cache method, is always the same regardless of any circumstances.
On the other hand, cache #user would provide slightly different behaviour. Each user who opens users.haml page would see proper greeting based on his/her role. The reason for this behaviour is that digest differs for all objects of type User, so cache_digests generates N cached pages for N users.
The last one kind of conditionals which comes to mind is the one based on conditional partials rendering, e.g.:
# users.haml
- cache [#user.month_of_birth]
- if #user.month_of_birth == 'October'
= render 'partial_one'
- else
= render 'partial_two'
So, this one renders correct cached page for users with different months of birth. But what should happen if I change the contents of partial_one? How does cache_digests understand that cache should be invalidated for those who were born in october (based on the conditional statement)?
The answer here is that it doesn't know that at all. The only thing it knows is that users.haml depends on both partial_one and partial_two, so changes to either of these inner partials gonna invalidate ALL the users.haml page caches regardless of users' month of birth.

Keeping track of user ID with custom authentication

I am currently making an app on apex.oracle.com, and I've been trying to solve this for a couple hours now, but I have no idea how to.
Alright, so basically my application has custom authentication based on a user table I created inside of my application. Therefore, it seems to render useless most APEX_UTIL functions to retrieve info on the current user. The issue is, I am trying to find a way to store the user's numeric ID from my table in the session, so I could retrieve it directly in the queries throughout my application, in order to do something like WHERE id = :MEMBER_ID instead of WHERE UPPER(username) = UPPER(:APP_USER).
Now, the way I attempted to do this is by creating a Post Authentication procedure that retrieves the user ID based on the username, and stores that value in the session using APEX_UTIL.SET_SESSION_STATE( p_name => 'MEMBER_ID', p_value => member_id ). However, it seems that SET_SESSION_STATE is unable to create custom session values or something, returning an ERR-1002 every time I use a value name that isn't specifically mentioned in the documentation.
I am a total newbie to APEX so I am probably unaware of something, however I have done many searches, but I could not find anything specifically related to my issue.
Thanks a lot if you can help.
You're trying to store a value into an item, whether page or application level. This requires that the item with that name exists in one of those scopes. So, do you have an item somewhere that is called MEMBER_ID?
I'd suggest you create one in the application scope. Go through Shared Components > Application items. Once created, you should be able to assign a value either through apex_util.set_session_state or with bind variable syntax eg :MEMBER_ID := somevariable;
There are a number of ways you can do this. Some have already been suggested in other answers.
Application Item (as per Tom's answer)
PL/SQL package global (as per hol's answer) - although you'd have to reset it for each call (e.g. by adding code to the application's Security Attribute Initialization PL/SQL Code and clearing it by adding code to Cleanup PL/SQL Code).
Globally Accessible Context - this method, while a little more complex, has some benefits especially for security and debugging. I've described it here: http://jeffkemponoracle.com/2013/02/28/apex-and-application-contexts/, but basically:
Create a globally accessible context:
CREATE OR REPLACE CONTEXT MY_CONTEXT USING MY_PACKAGE ACCESSED GLOBALLY;
In the post-authentication procedure (in the database package MY_PACKAGE), you can store the data you wish to keep track of, e.g.
DBMS_SESSION.set_context
(namespace => 'MY_CONTEXT'
,attribute => 'MEMBER_ID'
,value => '12345whatever'
,client_id => v('APP_USER') || ':' || v('APP_SESSION'));
(note the caveats in my blog article and the subsequent comments from others about CLIENT_IDENTIFIER not being reliably set at the post-auth stage)
In your views, code, etc. you can access the MEMBER_ID by simply referring to SYS_CONTEXT('MY_CONTEXT','MEMBER_ID').

MVC user's full name in Url, how to handle duplicates

I want to setup the following url in my MVC4 website, using the user's full name in the url:
http://www.myapp.com/profile/steve-jones
I have setup the following route in Global.asax:
routeCollection.MapRoute(
"profile", "profile/{userName}",
new { controller = "myController", action = "profile", userName = string.Empty
});
And I can take the parameter 'steve-jones' and match it to a user with matching name. My only problem though is, what if there is more than one 'Steve Jones', how can I handle this?
Does anyone know of a workaround/solution to this so that I can use a user's full name as part of the url and still be able to retrieve the correct user in the controller method?
Am I forced into including the user's id with the url (something that I do not want to appear)?
The usual way of handling this is by appending a number when creating the profiles. So if "steve-jones" is already a name in the database, then make the user's display name "steve-jones2". You basically have to insist that all profile urls are unique, which includes updating any existing database and account creation code.
Alternatively (and/or additionally), if two same names are found then have the script reroute to a disambiguation page where the user is presented with links and snippet of profile info of the many existing Steve Joneseses so they can go to the full correct profile.
Another way of handling it is by giving all user profiles an additional numeric code on the end. At my university all logins are based on name, so they give everyone pseudo-random 3-digit extensions so that they are safe as long as they don't get 1000 people with the exact same names :)
Some people might be happier being steve-jones-342 if there is no steve-jones or steve-jones1, if you're concerned.