Yesod auth: redirecting user to registration page - authentication

I'm playing with scaffolded site and i want to send user to the registration page after he logged in for the first time with OpenID or Google Account.
I've came up with this:
getAuthId creds = runDB $ do
x ← getBy $ UniqueUser $ credsIdent creds
case x of
Just (Entity uid _) → return $ Just uid
Nothing → do
return $ Just $ Key (PersistInt64 0)
And in HomeR handler i check for UserId value, showing registration form in case of zero.
This approach works, but seems hackish. What is the proper way to deal with such problem?

I would recommend splitting the information up into two entities: a User entity that tracks the user's credentials, and a Profile entity containing the registration information. For example:
User
ident Text
UniqueUser ident
Profile
user UserId
displayName Text
UniqueProfile user
In getAuthId, you'll either return an existing UserId, or if one doesn't exist, create a new entry. In HomeR, you'll get if there's a Profile (getBy $ UniqueProfile uid), and if not, display the registration form.

Related

How to guarantee preferred_username unicity on amazon-cognito?

I am using a single table to store all my data in dynamodb as such:
Partion Key (PK)
Sorting Key (SK)
Attributes
USER#gijoe
PROFILE#gijoe
{ name: "G.I", lastName: "Joe" }
USER#gijoe
CARD#first-card
{ name: "King", picture: "./king.png" }
I am using the preferred_username as a part of the Partition Key, and thus need it to be unique to avoid colliding user data.
How can I garantee that two users in my User Pool cannot have matching preferred_username ?
Edit:
The answer from #Lukas did it. Note that it required me to drop and recreate my cloudformation stack, which is why it failed on my first tries. Now when I try to edit the preferred_username I get the error I was looking for:
{
"message": "Already found an entry for the provided username.",
"code": "AliasExistsException",
"time": "2021-01-19T09:36:47.874Z",
"requestId": "7b52dbc2-58c5-4354-aa51-66d4dc7472a0",
"statusCode": 400,
"retryable": false,
"retryDelay": 85.84051584672932
}
username is unique within single pool. Same with alias. preferred_username may be configured as username alias.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html
Key take aways:
Developers can use the preferred_username attribute to give users a username that they can change. For more information, see Overview of Aliases.
The username must be unique within a user pool. A username can be reused, but only after it has been deleted and is no longer in use.
You can allow your end users to sign in with multiple identifiers by using aliases.
The preferred_username attribute provides users the experience of changing their username, when in fact the actual username value for a user is not changeable.
If you want to enable this user experience, submit the new username value as a preferred_username and choose preferred_username as an alias. Then users can sign in with the new value they entered.
If preferred_username is selected as an alias, the value can be provided only when an account is confirmed. The value cannot be provided during registration.
....
Alias values must be unique in a user pool. If an alias is configured for an email address or phone number, the value provided can be in a verified state in only one account. During sign-up, if an email address or phone number is supplied as an alias from a different account that has already been used, registration succeeds. Nevertheless, when a user tries to confirm the account with this email (or phone number) and enters the valid code, an AliasExistsException error is thrown. The error indicates to the user that an account with this email (or phone number) already exists. At this point, the user can abandon the new account creation and can try to reset the password for the old account. If the user continues creating the new account, your app should call the ConfirmSignUp API with the forceAliasCreation option. This moves the alias from the previous account to the newly created account, and it also marks the attribute unverified in the previous account.

Problems adding items with contact references when authenticated as an app

I'm trying to add an Item, where one of the fields is of type contact (user), to Podio.
I do not have the contact profile_id, only the name, so I need to search the contact to get the profile_id before adding.
The problem is that the /contact/ resources are inaccessible since I'm using app authentication.
The error is: "Authentication as app is not allowed for this method"
What is the recommended way to do this?
Thanks.
As I can see, the tricky part here is that you have just a name of the user. So you need to search this name first.
To be able to search you should be authenticated not as an app, but as a user with appropriate rights. I believe this is because search functions a rate-limited per user. You may authenticate on client side, server side or just by entering user's email and password (see documentation here).
Then, when authenticated, just use search functions with the parameter "ref_type": "profile" to look for the user name within space, organisation or globally. Example for PHP-client:
$attributes = array(
"query" => "John Doe",
"ref_type" => "profile"
);
$results = PodioSearchResult::space( $space_id, $attributes ); // search in space
$results = PodioSearchResult::org( $org_id, $attributes ); // search in organisation
$results = PodioSearchResult::search( $attributes ); // search globally
Functions above will return an array of the most relative results found. There you can get a user id and other user info.
Note that technically several different users may have the same name, so there might be more that one result found. It will be up to you to choose one of them somehow.

Authentication in liferay pages

We are having a portlet on a liferay page. We want to put up up a permission on every action method that is performed. For example on page A we have landed an XYZ portlet. Now we want that whenever there is any action performed form this portlet, we want to check that if the user is having a role to perform this action or not.
It wont be a good approach to put up the code in Action method of the portlet cause we are having approximately 20 such pages and portlets.
Can we have some sort of filter or so, so that each action request is checked if the user is having the access to the content or not.
Thank you...
My idea.
Use a filter to intercept all request
You can add a filter to the Liferay Servlet to check every request.
For that you can use a hook-plugin.
Look at this :
http://www.liferay.com/fr/documentation/liferay-portal/6.1/development/-/ai/other-hooks
http://connect-sam.com/2012/06/creating-servlet-filter-hook-in-liferay-6-1-to-restrict-access-based-on-ip-location/
Issue with filter is that you can't access ThemeDisplay or use PortalUtil.getUser(request).
So you must use work around like that :
private User _getUser(HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
User user = PortalUtil.getUser(request);
if (user != null) {
return user;
}
String userIdString = (String) session.getAttribute("j_username");
String password = (String) session.getAttribute("j_password");
if ((userIdString != null) && (password != null)) {
long userId = GetterUtil.getLong(userIdString);
user = UserLocalServiceUtil.getUser(userId);
}
return user;
}
Filtering the request
To filter the request you must get :
page id (Layout id in Liferay)
portlet id
portlet lifecycle
One more time using a filter is a pain because you can get the ThemeDisplay. These params are easy to get (with real object instancee) with ThemeDisplay.
So you must get this as parameter in the request.
final String portletId = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_id", "");
final String layoutId = ParamUtil.get((HttpServletRequest) servletRequest, "plid", "");
final String portletLifecycle = ParamUtil.get((HttpServletRequest) servletRequest, "p_p_lifecycle", "");
Lifecycle details :
portletLifecycle is a int and the meaning of value is :
0 : RENDER
1 : ACTION (the one that interests you)
2 : RESOURCE
I think that with this data you can be able to define if user can or cannot make the action.
You can get user roles from the user.
You can get the current page and portlet linked to the request.
And you can know if the request is an action request.
Good luck with Liferay.
You can add freely configurable permissions to Liferay, see the Developer Guide for detailed information. My first guess on this would be that these affect "model resources", e.g. the data that your portlet is dealing with, rather than portlet-resources, e.g. permissions on the individual portlet itself. Think of portlet-permissions as permissions that are defined by Liferay, model-resources as permissions where you can come up with your own vocabulary on the actions, e.g. "UPDATE_ADDRESS" etc.
These permissions will typically be tied to roles, which are granted to users/usergroups/etc.
Based on this variability, it depends on the nature of your permissions if you can write a filter to generically check permissions, or if it depends on more than the individual action call.
If you determine that there is a generic solution, look up PortletFilters, they behave just like ServletFilters. These can easily provide a home for permission checks.
It's quite hard to cover this topic in such a short answer, I hope to have given enough resources for you to continue your quest.
You can abuse some existing portlet permission like "Add to Page" and set it to roles that should call the action.
And by the rendering and action phases validate "has the user necessary permission".
Or you can create new permission and configure it by portlet-configuration. This way is cleaner, but difficulty.

Show specific data to a user after log in

I am using simple membership provider and mvc. After a user registers he/she will be redirected to a view called registerDepartments. Incase time is an issue the user can login at a later stage. How do I retrieve the data registered by the user previously. In my previous workings it shows all the data from the table for all users who registered a branch.
I didn't get your question clearly, because you didn't show any code of your work and any example.
However I think you want to retrieve branches created by a user for him/her right? If So, you can use something like the following:
var currentUserId = (int)Membership.GetUser().ProviderUserKey;
var branches = dbCtx.Branches
.Where(b => b.CreatorUserId == currentUserId).ToList();

How to model restrictions on data visible on resources?

How to model restrictions on data visible on resources? Different people are accessing the same resources but with different roles so they are not allowed to see all the information.
The case I am working on:
Solution without access restriction on information:
User:
name
phoneNumber
If anyone could access it this would be easy to model as:
GET /User -> [{name:"John", phoneNumber: "322-333"}]
GET /User/{id} -> {name:"John", phoneNumber: "322-333"}
However, say I have two roles, admin and user. The phoneNumber must only be visible to users who are also admins. Authorization token is transmitted in a cookie, header or similar. The server will know which roles a requester has. How would one design an API to handle this? I have a couple of ideas:
1) The naive solution would be to just filter it and leave the fields unset if you arent allowed to access it ie.
If user: GET /User -> [{name:"John"}]
If admin: GET /User -> [{name:"John", phoneNumber: "322-333"}]
2) Embed the role in the url:
If user is wanted as a User: GET /User/User -> [{name:"John"}]
If user is wanted as an Admin: GET /Admin/User -> [{name:"John", phoneNumber: "322-333"}]
3) Define a new resource for each possible subset of fields:
If user is wanted as a User: GET /PublicUserInfo -> [{name:"John"}]
If user is wanted as an Admin: GET /FullUserInfo -> [{name:"John", phoneNumber: "322-333"}]
Would a different approach be better ?
Does anyone have experience with a solution that worked out in practice?
Use option 1 based on the authenticated user. If you opt for 2 or 3 clients implementing your API have to worry about twice as any API endpoints and when they should be used.