In one application I need to create two types of accounts, let's say users and tutors.
Each of them should be able to register/login/... user/tutor account defined by different tables in db.
e.g.
[app]/user/register should provide a form with fields: username, email, password, hobby
[app]/tutor/register should provide a form with fields: username, email, pass, name, surname, telephone
Web2py auth service allows using auth_user table which can be customized. Is there a way to use two tables separately according to controller in one application?
Field auth.settings.table_user contains reference for auth_table but I probably shouldn't use it for this purpose.
The Auth system isn't designed to work this way. Instead, it would probably be better to put all the fields for both user types in the single auth_user table and then selectively set the readable and writable attributes of the fields to True or False depending on the context.
In the model file defining Auth:
user_extra_fields = [Field('hobby'), ...]
tutor_extra_fields = [Field('telephone', ...]
auth.settings.extra_fields['auth_user'] = (
[Field('user_type', requires=IS_IN_SET(['User', 'Tutor']))] +
user_extra_fields + tutor_extra_fields)
In the controller that manages the Auth functions:
def user():
if request.args(0) in ['register', 'profile']:
hidden_fields = (user_extra_fields if request.args(1) == 'tutor'
else tutor_extra_fields)
for field in hidden_fields:
field.readable = field.writable = False
return dict(form=auth())
Related
I have a table that describes a document with title, content, created_at, etc and a struct that describes columns to filter that table
struct Partial {
handle: String,
title: String,
content: String,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
}
which; after some transformations, I pass to the database layer and depending on who the user is, return a set of rows.
For example, if user is the author, user can search any columns, otherwise they can search only publicly visible documents.
A user can also 'share' a document with another user so if there a share from the requester to the document owner, the user should also be able to search in those documents as well.
There are two ways to go about that (as far as I know)
Use inner joins and filter on columns
INNER JOIN author on document.id = author.document_id
INNER JOIN visibility on document.id = visibility.document_id
INNER JOIN share on document.id = share.document_id
...
...
...
WHERE document.handle = $handle -- from `Partial` struct
WHERE document.title = $title -- from `Partial` struct
WHERE document.content = $content -- from `Partial` struct
WHERE author.user_id = $user_id -- from user info passed to db
WHERE visibility.scope = 'PUBLIC' -- from user info passed to db
WHERE share.shared_to = $user_id -- from user info passed to db
WHERE group.member = $user_id -- from user info passed to db
...
...
...
This requires passing user information ($user_id for this example but this also sometimes includes additional info like if the user is blocked or the account is non-public, belongs to a group, etc. Around 12 properties in total) to the database layer.
generate a list of ids in application layer and pass it on to the database layer.
For example,
let public_document_ids = visibility.find(Find { visibility: Visibility::Public }).map(|visibility_row| visibility_row.document_id).collect();
let authored_document_ids = author.find(Find { document_author: user_id }).map(|authored_row| authored_row.document_id).collect();
let shared_document_ids = share.find(Find { shared_to: user_id }).map(|share_row| share_row.document_id).collect();
and then pass sum of all of these ids to database
WHERE document.handle = $handle -- from `Partial` struct
WHERE document.title = $title -- from `Partial` struct
WHERE document.content = $content -- from `Partial` struct
WHERE document.id IN $ids -- from `ids` passed to db
...
...
...
To me it seems that the first approach of using inner joins and filtering on columns in the database can
potentially be more efficient,
and a lot more easier to get right
but the second approach, generating a list of ids in the application layer, allows
a lot more flexibility and
also lets me use different (distributed) databases to store different tables (perhaps even different database drivers)
It also reduces the amount of user information passed to the database layer
but it also requires loading all the data into the application layer before filtering the results
and implementing pagination will be lot more complex
So my question - is there a way to know which approach I should go with? Perhaps there's something I haven't considered yet.
While developing a multi-tenant app with ASP.NET Core I noticed that it brings 2 new indices: NormalizedUserName & NormalizedEmail.
The main problem is that it gets too difficult to have a unique user per tenant.
What I mean is having multiple users with the same UserName & Email but different TenantID.
In order to achieve this I have to remove those indices
public static void RemoveIndexes(this ModelBuilder modelBuilder)
{
modelBuilder.Entity<ApplicationUser>(entity =>
{
var normalizedUserNameIndex = entity.HasIndex(u => new { u.NormalizedUserName }).Metadata;
entity.Metadata.RemoveIndex(normalizedUserNameIndex.Properties);
var normalizedEmailIndex = entity.HasIndex(u => new { u.NormalizedEmail }).Metadata;
entity.Metadata.RemoveIndex(normalizedEmailIndex.Properties);
});
}
My questions are:
What is the purpose of these 2 new indices?
What would it affect if we just remove them?
Is there anything we need to pay close attention to after removing them? (e.g. overriding default UserManager functionality or something to that effect)
First of all, I wouldn't change anything of the Identity Framework if I can't oversee the effects. If you insist, you can test what happens yourself. But, do you need to remove the fields?
If the relation of user-to-tenant is one-to-many, then tenantId should not be a field of ApplicationUser but rather be stored in a seperate table, like UserClaims.
You can add multiple tenantId's as claim of the same type, like http://tenant.company.com/Id. It will then become a collection of values, like what happens with roles.
If you don't want this then you can use different claimtypes, like http://tenant.company1.com/Id, http://tenant.company2.com/Id or something like that.
You can choose to include only claims that are linked to the tenant, which could be determined from the site binding or the url, for instance.
This design allows the user to login using the same password everywhere. Please note, this is about identity: who is the user? The user doesn't need to have a different password for every tenant.
It also makes it easier to change a password. Because I wonder, how does your scenario look like with multiple user records for each tenant? Will you update all records at once when a password changes? And how will you support 'forgot password' and other features?
I have a Profile model with a OneToOne relationship with a User. For such Profile model I use the following class, which uses two additional fields coming directly from the User:
class ProfileResource(ModelResource):
username = fields.CharField(attribute='user__username')
email = fields.CharField(attribute='user__email')
class Meta:
# Base data
queryset = Profile.objects.all()
# Allowed methods
list_allowed_methods = ['get']
detail_allowed_methods = ['get', 'put', 'patch']
# Security setup (FEEBLE)
authorization = Authorization()
authentication = BasicAuthentication()
Such resource works fine when consulting profiles. It retrieves the username and email perfectly, and is capable of filtering and ordering by such parameters.
However, I cannot manage to update such fields on the User model in a, say, elegant fashion. All I have come up with is this:
def obj_update(self, bundle, skip_errors=False, **kwargs):
bundle = super().obj_update(bundle, skip_errors=skip_errors, **kwargs)
if 'username' in bundle.data: bundle.obj.user.username = bundle.data['username']
if 'email' in bundle.data: bundle.obj.user.email = bundle.data['email']
bundle.obj.user.save()
return bundle
Which works fine but doesn't seem the best of solutions.
Does anybody know a better way to work out this kind of field-only relationship between a resource and a related model?
I have written one extension for making service order.
The issue I am facing here is,
There are FE users belong to three FE user groups namely "client", "Admin" and "Employee".
Here the client can make order and he should be able to see only his orders.
And the admin can see all orders done by different clients.
And the employee should be able to see only some clients orders.
Currently I made a order table with N:1 relation with FE user table. So every order should relate with any one client.
So in controller, I am checking the login user and using custom query in repository, I am accessing order related to loggedin client (FE user)
In file OrdersController.php
public function listAction() {
$orders = $this->ordersRepository->orderForLoginUsr();
$this->view->assign('orders', $orders);
}
In file OrdersRepository.php
public function orderForLoginUsr(){
$loggedInUserId = $GLOBALS ['TSFE']->fe_user->user['uid'];
$query = $this->createQuery();
$query->matching(
$query->equals('user', $loggedInUserId)
);
$query->setOrderings(array('crdate' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING));
return $query->execute();
}
But here my question is how to make admin user able to see all the orders done by all clients?
I have to write different template and action that calling function findAll() ?
$orders = $this->ordersRepository->findAll();
And how to set for usergroup Employee ?
Thanks in Advance
I think that the easiest way is to actually implement 3 actions with 3 different plugins, something like: listClientAction, listAdminAction and listEmployeeAction
In each of those action, you implement a method in your repository that fetch the right list of order with the good ordering:
orderForLoginClient(), orderForLoginEmployee(), orderForLoginAdmin()
What does the trick actually is that there will be 3 plugins on your page, one for each action. In each instance of your plugin, you set the access for the right be_group.
Don't forget to add the actions and plugin in the localconf and ext_table files.
I hope it will help!
Olivier
If your view is almost the same for client, admin, employee you should simply add a method like getOrderWithPermissionsForUser($currentUser);
In the method itself you should check for the usergroup and call different queries on your Repo.
If your view is different from usergroup to usergroup, you should use different templates with partials for the same parts.
If the data of the views is the same, just change the template for each usergroup in the action. If not use different actions.
Here is a helper method for easily changing your templatefile.
/**
* This method can change the used template file in an action method.
*
* #param string $templateName Something like "List" or "Foldername/Actionname".
* #param string $templateExtension Default is "html", but for other output types this may be changed as well.
* #param string $controllerName Optionally uses another subfolder of the Templates/ directory
* By default, the current controller name is used. Example value: "JobOffer"
* #param \TYPO3\CMS\Fluid\View\AbstractTemplateView $viewObject The view to set this template to. Default is $this->view
*/
protected function changeTemplateFile($templateName, $templateExtension = 'html', $controllerName = null, AbstractTemplateView $viewObject = null)
{
if (is_null($viewObject)) {
$viewObject = $this->view;
}
if (is_null($controllerName)) {
$controllerName = $this->getControllerContext()->getRequest()->getControllerName();
}
$templatePathAndFilename = $this->getTemplateRootpathForView($controllerName . '/' . $templateName . '.' . $templateExtension);
$viewObject->setTemplatePathAndFilename($templatePathAndFilename);
}
I'm using OpenAM 9.5.2 for authenticating users on an application. The authentication works well but I'm having issues to get user memberships from final application.
I've defined the group "somegroup" in openam and added my user to this group. Now in my application, I want to test if authenticated users is member of this group. If I'm testing it with:
request.isUserInRole("somegroup");
I get false result. Actually, I have to test
request.isUserInRole("id=somegroup,ou=group,dc=opensso,dc=java,dc=net");
in order to get a true response.
I know that it's possible to define a privileged attribute mapping list in the sso agent configuration to map id=somegroup,ou=group,dc=opensso,dc=java,dc=net on somegroup, but it's not suitable in my situation since roles and groups are stored in an external database. It's not convenient to define role in database and mapping in sso agent conf.
So my question : is there a way to make openam use the "short" (i.e. somegroup) group name instead of its long universal id ?
This is not an answer, just one remark.
I've performed some researches in openam sources and it seems to confirm that the role name stored in repository is replaced by universalId when openam build the request. This is performed in com.sun.identity.idm.server.IdRepoJAXRPCObjectImpl.java class:
public Set getMemberships_idrepo(String token, String type, String name,
String membershipType, String amOrgName,
String amsdkDN
) throws RemoteException, IdRepoException, SSOException {
SSOToken ssoToken = getSSOToken(token);
Set results = new HashSet();
IdType idtype = IdUtils.getType(type);
IdType mtype = IdUtils.getType(membershipType);
Set idSet = idServices.getMemberships(ssoToken, idtype, name, mtype, amOrgName, amsdkDN);
if (idSet != null) {
Iterator it = idSet.iterator();
while (it.hasNext()) {
AMIdentity id = (AMIdentity) it.next();
results.add(IdUtils.getUniversalId(id));
}
}
return results;
}
To my knowledge this is not possible currently with out of box code. In case you have limited amount of groups, then privileged attribute mapping could be a way to go, but if not, then the issue gets more complicated.
You could try to change the AmRealm implementation (authenticateInternal method) to match your requirements and hook the new class into the container-specific ServiceResolver class (like http://sources.forgerock.org/browse/openam/trunk/opensso/products/j2eeagents/tomcat/v6/source/com/sun/identity/agents/tomcat/v6/AmTomcatAgentServiceResolver.java?r=700&r=700&r=700 )
You can also create a JIRA issue about providing a config property to put membership information into roles in non-UUID format.