User-dependent BAdI implementation. How? - abap

Is there any way to create user-dependent BAdI implementations? I mean that the BAdI has different implementations which are called depending on user which is logged and which calls the specific transaction.
Other scenario is to not call certain implementations for this user and to call for other user. Is is possible?
Now I'm using simple check
IF sy-uname = 'username'.

New BAdIs allow the GET BADI call to have one or more FILTERS parameters which can be used in the implementation definition to select different implementing classes. But these filter parameters must be provided by the code which calls the BAdI. When the standard code doesn't provide the username as a filter parameter, there is no way to choose an implementing class based on the username.
So when you want different logic for different users, you need to do this in your implementation code. But using the username to decide what to do might not be the most maintainable architecture.
I guess the reason why the BAdI is supposed to behave differently for a specific user is because that user has some special job in the company. What will you do when the person who has this position changes, or when he gets one or two other people to help them, or when he just calls in sick and someone else has to do his job? Do you want to transport a program change whenever that happens? But there are other options:
Different behavior by user group. You can read the user group from the database table usr02 (field CLASS)
Different behavior by permissions. Do an AUTHORITY-CHECK, and make the BAdI behave differently depending on the success.
Different behavior by user parameter. To read a user parameter in your program, use GET PARAMETER ID. The parameters of a user can be set by the administrators in the transaction SU03, by the users themself with the transaction SU3 (when they have the permission to do so) or programatically with SET PARAMETER ID.

Related

Ignore or not API endpoint parameters based on access level

I am working on an API endpoint that returns a list of products:
"api/products"
The endpoint accepts the following parameters:
page_size
page_number
Each product has a boolean property named IsApproved.
In the web application used by common users I always want to return only the Approved products ... On the web ADMIN application used by administrators I want to return all products, Approved or Not ...
My idea would be to add a new parameter (enumeration) named:
ApprovedStatus
And the values would be Approved, NotApproved and All.
On each API call I would check the user permissions ... If is admin I will consider the value on this parameter. If not then I will always return only approved products.
Another solution would be to have different endpoints ...
Any advice on which approach to take or is there other options?
The approval status is part of the product, therefore, in a perfect REST world, you don't want a different endpoint at all since you're accessing the same resource.
Then, for filtering a resource based on a property value, I think the convention is that if you specify that property as a query parameter it will only return those matching the value, and if not, it will return all of them, so I don't see the need to define a special ApprovedStatus parameter with some special values. Just query by isApproved!
Finally, about how to handle authorization. This, I think, should be handled at a completely separate layer**. If authorization is involved, you should have an explicit authorization layer that decides, for a specific resource and user, wether access is granted or not. This means the query would be triggered and if one of the resources generated by the query fails to be authorized for the user that triggered the query, it's taken out of the results. This accomplishes the behaviour you want without having any code that is checking specific users against specific query parameters, which is good because if tomorrow you have another endpoint that exposes this objects you won't have to implement the same authorization policy twice. Pundit is a perfect example on how to do this with Ruby elegantly.
**Of course, this approach retrieves data from the database unnecessarily which could matter to you, and also opens your endpoint up to timing attacks. Even then, I would consider tackling these problems premature optimizations and should be ignored unless you have a very good reason.
You're right about your ideas:
You can create a new endpoint just for admins, that will return all products
You can use a kind of authorization (e.g. Authorization Header) in order to check if the API is being called through admin or normal user. Then you can route internally to get all products or just IsApproved products.
You can add a proxy in front of your API to route to the right action, but it can also be achieved directly in the API but I think the second solution is easier.
Adding one more property is a bad idea.
In my opinion, adding another end point is very good. Because it will increase the protection in the admin end point.
Otherwise, since it is a web application, Simply set a cookie and a session to identify and separate the admin and user.
Going with the principle of least astonishment, I'd be in favour of adding a second endpoint for admin users. Such that you'll have:
GET /api/products (for regular users)
GET /api/admin/products (for admins)
This allows your code and API documentation to be nicely separated, and all of the admin-specific authentication details can live under the "admin" namespace.
The intention behind each API call is also clearer this way, which helps developers; and means that you can differentiate between admin vs regular usage in any usage stats that you track.
With ApprovedStatus, I think the specifics here don't matter much, but - considering what a developer using the API might reasonably expect / assume - it would be good to:
Ensure the ApprovalStatus parameter name matches the property name for "approval" that you return with each product object
Defaults to "approved" if it is not specified
Alert the user when an invalid value is specified, or one that they don't have access to
Bottom line: to answer your headline question - I think it's bad practice to ignore user input... sometimes. Design your API such that distinctions around when input can be passed in is very clear; and always alert the user if you receive input values that are technically acceptable, but not in the way that the user has requested, or for their access level. Ignoring values that are plain wrong (e.g. an argument that doesn't exist) is another story, and can be useful for future proofing or backwards compatibility.

REST API optional path elements

I am in the process of designing a new API and am trying to make it as simple as possible. For the intended system most API consumers will be referencing objects that belong to them alone however a few other accounts will "own" objects in other peoples accounts. The questions is whether account becomes a required part of the path or an optional inclusion for these special super-accounts.
Here is an example spec and works fine in the single user context (Account ID "basic1"):
# return person "s4t4s2" for account "basic1"
GET /v1/person/s4t4s2
# return team "g3a35a" for account "basic1"
GET /v1/team/g3a35a
For super-accounts they have their own objects where the above implementation works, however they also require access to the properties of accounts they effectively own (Account ID "super1"):
# return person "s4t4s2" for account "super1"
GET /v1/person/s4t4s2
# get team "g399a2" for account "super1"
GET /v1/team/g399a2
# return person "s4t4s2" for account "basic1"
GET /v1/accounts/basic1/person/s4t4s2
Because most of my consumers will be dealing with the single account view is it best practice to use the second format for all accounts or is it entirely valid to use both formats with automatic scoping via authentication credientials when the account is omitted?
If I understand correctly, those are the same "person" resources, but they have multiple URIs? I would probably prefer having one URI for a single instance of a resource. Even if it has different "views". Different views can still be solved by either having different media-types, or just filling out fields differently on the same media-type depending on the user's permissions.
The advantage of having a single URI for a single instance is that it can be bookmarked, cached, etc. For example if a user's team/account view changes, it can't reuse its links to persons because the URIs change. I think that is not a good design.
If my understanding is wrong, and /v1/accounts/basic1/person/s4t4s2 is not the same person as /v1/person/s4t4s2 then disregard my comment. :)

Authorization in Pentaho

Is it possible to show the reports filtered by a field (say location) for a user in pentaho?
For eg:
UserA manages RegionA
UserB manages RegionB
UserAB manages RegionA and RegionB
When a user log into the system, (s)he should see the report showing the region only that they manage.
A same report format is shown for all users, but the content (or Query filter) differs for each users.
If this is possible, how to implement this?
Thanks for your assistance.
Yes; The proper way to do this is with session startup actions. These xactions allow you to set session variables which you can then access in your report. In this case you could define a location and use that in your query.
http://wiki.pentaho.com/display/ServerDoc2x/Using+System+Actions+to+Control+Data+Access
Alternatively you can access the username via a session variable too, so you could always put the logic in the query. but the nice thing about the session startup actions is that the logic is contained in one place incase it needs to change.
In your case as this is MDX (which i only just noticed from the tag) you'll have to generate a string that looks like a set of locations.

Yii RBAC, Role change in runtime

I am building up a dynamic RBAC system for Yii and I don't know how to handle this problem:
The moderators can change the roles of the Users, furthermore the User can change it too by getting a different qualification (let's say achievement, so s/he can do more stuff and it can happen both ways).
What happens, when the role is changed Backwards (a role with less right) or Forwards (a role with more right) when s/he is logged in? Cannot access the functions he just got the right to use? Or can still access the functions until a logout/relog action?
Thanks your help in advance.
The effect of changing the authorization assignment will be inmediate.
Only the successive calls to IWebUser::checkAccess() issued in the same request may return cached values, since the default implementation of IWebUser, i.e. CWebUser, uses a static attribute to cache the calculated permissions.
To clarify the procedure, you will be calling IAuthManager::revoke() on the old permissions and IAuthManager::assign() on the new ones.
Edit
Sometimes you store session information through the IWebUser::setState() method; if the state of the currently logged user shall change along with the permissions, e.g. you store the current user's role name, you must take this into account and either call IWebUser::clearState() or IWebUser::logout() followed by IWebUser::login() –the latter also clears the cached permissions in the CWebUser implementation.
CWebUser::_access is declared private, so you will have to declare a new attribute if you want to override the default implementation.

OOP Design: Where should reusable validation be?

I've got a wizard which needs to validate if a user is logged in and then at the end validate that all the details they have entered are correct.
The issue is I'm not sure where to put the validation logic.
At the moment I have a BuyMembership class which has validation on the Buy() method.
However this won't be called at the beginning of the wizard where I need to validate whether the user is unique and is eligible for buying a membership.
For this validation I've created a BuyMembershipValidation class which validates if the user is eligible.
The issue now is that I have to pass a different parameter object to the BuyMembershipValidation and the BuyMembership classes. This means the data is split.
Is there a better way of doing it. Should I only load part of the information into the BuyMembership class for the initial validation and then load the rest after?
Update:
I need to validate when they enter the wizard (check if they are logged in already) and if they aren't then they will register as a new user otherwise I have to check if they have the right settings to buy membership as all users can't buy a membership. That's why I need two sets of validation. One for whether they're eligible and another for the actual data they enter to register. However I would like to recheck that they are eligible when they make the final transaction just in case they somehow made it past the first wizard step or if the web service (where I'm doing the logic) is called from somewhere else at a later point.
Another Update:
I've added an answer with my decision.
TIA,
Jonathan.
You're actually talking about three very different things here, and like Allain said, you need to think along the lines of single-responsibility to do this right, OOP-like.
Authentication: making sure a user is known
Authorization: making sure a user can access certain logic in your application
Validation: making sure the data a user inputs is valid/safe/what's expected or needed
A validator is typically tasked with validating user supplied information, not authorization. The single responsibility principle dictates that in your case they should definitely be handed by two objects.
Passing different parameter objects is fine, but but I think the authorization one shouldn't really be passed in to the BuyMembership object, it sounds like something that should be handled externally to it.
My 2 cents.
User authentication is a broad topic. It really depends on what type of application you are building. If it is a web application, I would recommend using one of the standard methods for authentication: NTLM / kerberos
As to when to validate, I don't see any problem with holding off on the validation until the actual Buy method is called. As long as during the steps in the wizard, you are not releasing any information to them and are only collecting data from a form. The main time when you need to be concerned about authentication is when you are actually placing an order or releasing information to the user that should be locked away in a database somewhere.
If you provide more details to what you are trying to do, I will refine my answer to help you a bit more. It's a rather broad question though, so this is the best I can come up with for now.
I've decided to go with just the one class. The reason is that there is common logic / behaviour in the IsEligible and Buy methods. One determines if the inputs meet the eligibility requirements and the other validates that all the details needed to buy the membership are valid. I don't really see this as authentication related. The authentication is done in a separate web control and I just check the Identity.IsAuthenticated and Username to see if they are logged in. I then check their username and if they are eligible to buy then I let them continue, otherwise show an error message. In terms of input validation I already have validation controls on my webpage but the validation I'm concerned with here is server side business logic validation. For that reason I think it should be in the business logic class. Not some validation class separate from the logic that performs the payment. This may just be my way of looking at it but I think this fits well with the BDD ideas of keeping validation inside the entity that holds the data.