Custom fields with Membership provider - security

I am trying to use the custom membership provider with my custom user class from my dbcontext but I am experiencing problems when trying to create a new user using the "Register" form. I have fields such as Points etc in my user class that need to be initialised upon creation of the user.
When I call:
WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
There is no way for me to add these details in. How can I use this so that I can autopopulate these values with zero for instance.

Have you checked out this overload which allows you to specify custom properties:
WebSecurity.CreateUserAndAccount(
userName: model.UserName,
password: model.Password,
propertyValues: new
{
foo = "bar",
baz = "bazinga",
}
)

Related

custom fields for address are not being saved?

I added some custom fields for address for in a twig template which extends from
#Storefront/storefront/component/address/address-form.html.twig
when i submit the form from the /checkout/register page, the custom fields are not saved.
In the Shopware\Core\Checkout\Customer\SalesChannel\RegisterRoute.php file, when data mapping is done for address, only the fields below are mapped:
private function mapAddressData(DataBag $addressData): array
{
$mappedData = $addressData->only(
'firstName',
'lastName',
'salutationId',
'street',
'zipcode',
'city',
'company',
'department',
'countryStateId',
'countryId',
'additionalAddressLine1',
'additionalAddressLine2',
'phoneNumber'
);
if (isset($mappedData['countryStateId']) && $mappedData['countryStateId'] === '') {
$mappedData['countryStateId'] = null;
}
return $mappedData;
}
How can i save custom fields for an address when developing an app? is it possible ?
There's this repository (not mine) with an example on how to add additional inputs to the storefront and how to map them to the payload when persisting the address. Basically you subscribe to the corresponding CustomerEvents, take the user inputs and enrich the payload.

How to restore persistence properties that the domain model does not have

I have been thinking and reading about this for some time with not success.
I'm in the process of separating the db layer from the domain (I was using orm entities as domain entities).
To do it I have created simple mapper classes with methods to transform a persistence entity to a domain entity and viceversa.
The problem comes when a domain entity does not have all the persistence properties as they are useless for the bussiness logic (for example the user logo image url).
I map the user db result to its domain entity without saving the logo so when I try to convert it back to persistence I don't have a way to retrieve again the logo.
My question is, there is a pattern to solve this or a domain entity does have to always contain all the necessary info to be persisted even when that info is completely useless for the bussiness logic?
Code example:
// User persistence entity
class User {
name
is_active
account_id
logo
}
// User domain entity
class User {
name
isActive
account
}
// Mapper
class UserEntityMapper {
static toDomain(raw) {
return new User({
name: raw.name,
isActive: raw.is_active,
account: raw.account_id,
})
}
static toPersistence(User domain) {
return {
name: domain.name,
is_active: domain.isActive,
account_id: domain.account,
logo: // How tf can i get the logo here????
}
}
}

Custom User Registration: How to write gender to customer record

My user registration form has a custom field for Gender. How can I make SCA write the customer's gender to the NS database?
Should I use Commerce API or SuiteScript API?
I guess I should edit the model Account/SuiteScript/Account.Model.js but should I use the Commerce API (Customer object or the Profile/User object) or directly write to the database using record.setFieldValue()?
In Account/SuiteScript/Account.Model::register() function:
//#method register
//#param {UserData} user_data
//#param {Account.Model.Attributes} user_data
register: function (user_data)
{
var customer = session.getCustomer();
....default user registration code
var user = Profile.get();
// Should I do it like this?
customer.updateProfile({
"customfields": {
"custentity_dev_gender": user_data.gender
}
})
// Should I edit the user object??
// Should I interact directly with the customer record. Does Commerce API allow this??
var record = nlapiLoadRecord('customer', user.id);
record.setFieldValue('custentity_dev_gender', user_data.gender);
var internalId = nlapiSubmitRecord(record);
return {
touchpoints: session.getSiteSettings(['touchpoints']).touchpoints
, user: user
, cart: LiveOrder.get()
, address: Address.list()
, creditcard: CreditCard.list()
};
}
Its Very Simple actually no need to write record.setFieldValue()
Very first step is to create custom entity field from Customization--> Entity Field-->New
Apply the entity field to customer give edit access to field and Save.
Copy the entity id from there, in this case say 'custentity_gender_field'
The you need to override below two files, if you don't know ask separate question I'll reply there.
i.Modules/suitecommerce/LoginRegister#2.2.0/Templates/login_register_register.tpl
ii.Modules/suitecommerce/Profile#2.3.0/SuiteScript/Profile.Model.js
Go to first file write below code somewhere where you need to put gender
<div class="login-register-register-form-controls-group" data validation="control-group">
<label class="login-register-register-form-label for="custentity_gender_field">{{translate 'Gender: '}}
<small class="login-register-register-form-optional">{{translate '(optional)'}}</small>
</label>
<div class="login-register-register-form-controls" data-validation="control">
<input type="text" name="custentity_gender_field" id="custentity_gender_field" class="login-register-register-form-input">
</div>
</div>
Then Go to Second File i.e. Profile.Model.js
You can see in get function various filed values, put your custom field value there
this.fields = this.fields || ['isperson', 'email', 'internalid', 'name', 'overduebalance', 'phoneinfo', 'companyname', 'firstname', 'lastname', 'middlename', 'custentity_gender_field', 'emailsubscribe', 'campaignsubscriptions', 'paymentterms', 'creditlimit', 'balance', 'creditholdoverride'];
Deploy your code and test under user roles--> Manage User
I missed one more thing here, you need to make changes in
Account#2.2.0/SuiteScript/Account.Model.js as well in this model find
register: function (user_data)
{
}
and your custom field in
ModelsInit.session.registerCustomer(
{
custentity_gender_field:user_data.custentity_gender_field
});
That's it...

How can I automatically assign a role to a CouchDB user?

I have the following method for registering users:
// Registration method
exports.register = function(req, res) {
var username = req.body.username,
password = req.body.password,
first = req.body.first;
last = req.body.last;
role = req.body.institution;
nano = require('nano')('http://127.0.0.1:5984');
var users = nano.use('_users');
var user = {
"_id": "org.couchdb.user:" + username,
"name": username,
"type": "user",
"roles": [],
"password": password,
"realname": first + " " + last
};
users.insert(user, function(err, body) {
if(err) {
res.send(err.reason, err.status_code);
} else {
res.send(body, 200);
}
});
};
As you can see, I have a variable called role which I would like to set in the "roles" array. Unfortunately, as you probably know, CouchDB only allows the admin to set roles. I'm okay with this, but I'm using the roles as institutions, so I'd like a registering user to be able to pick what institution they're registering under and therefore it should automatically be assigning them this role. Without hard-coding my admin credentials into this method, i.e.
nano.config.url = "http://admin:password#127.0.0.1:5984/"
How can I automatically assign a new user to their specified role as I've mentioned?
The roles array is used by CouchDB to decide database authorization. This allows you to configure access to a given database by a user's role. If the user can set their own role then somebody can give themselves access to any institution. They can even make themselves an admin, which defeats the purpose of protecting your admin password.
I'm not sure what your use-case is, but I don't think you are trying to prevent access to other institutions. Rather, I suspect you are trying to use the roles to perform some other application function (e.g. filtering out information for them to simplify what they see). Since you are wanting to perform a non-security function, I strongly suggest that you just add your own field to the users documents. You could add an institutions property to each user.
If you really want to do this, there is a way. The error you are receiving is coming from an update validation function in _design/_auth in the _users database. As an admin you can change this function.
Download the document. Open the file and search for Only _admin may edit roles to see where the test is being performed. Remove that conditional and save the document back to CouchDB.

How to access attributes of an embedded document (DBRef-like) from a Schema method with Mongoose?

I am building a nodejs app with express and mongoose/mongodb.
In order to manage user roles (a user may have 0-n roles), I decided to implement a User schema and a separate Role schema and tie them together like DBRefs and using mongoose populate capability to get from one to the other easily. I chose this structure because I thought this was the best way to answer things like: "Get me a list of users that have X role" or "Get me a list of users whose Y role is set to expire soon".
These are stripped down versions of my schemas for role and user:
RoleSchema = new Schema {
_user: { type: Schema.ObjectId, ref: 'User' }
type: String
expiration: { type: Date, default : '0' }
}
UserSchema = new Schema {
email: { type: String, index: { unique: true }}
roles : [{ type: Schema.ObjectId, ref: 'Role' }]
}
With this, I am able to create/fetch/populate users and roles from within my express scripts without a problem. When I want to add a role to a user, I create the role, save it and then push it to the roles array of the user and then save the user. So, what happens in mongoDB is that each schema has its own collection, and they point to each other through an id field.
Now, what I want to do next is implement boolean-type methods on my schema to check for role-related issues, so that I can do things like
if(user.isActiveSubscriber())
I thought that I would be able to accomplish this simply by adding a method to my User schema, like this:
UserSchema.method "isActiveSubscriber", ->
result = false
now = new Date()
#roles.forEach (role) ->
result = true if role.type is "SUBSCRIBER" and role.expiration > now
result
My problem is that roles are coming out with empty attributes. I suppose it makes sense, since my user collection only has the id of the role but the actual attributes are stored in another collection.
So, here go the questions:
a) Is there a way to load the roles attributes from within my user schema method?
I tried calling populate inside the method, but got an error that the user object doesnt know any populate method. I also tried doing a Role.findById() from inside the method but also get an error (tried with Role and RoleSchema)
b) In case there is not a way ... should I simply add the code to check in my scripts? I hate having to put this kind of logic mixed with application logic/flow. Is there a better option?
c) Was it a bad idea to separate these collections in two? Am I completely missing the point of NoSQL? Would it be better if roles were simply an array of embedded documents stored as part of the user collection?
Let me answer your questions:
a) What you can do is to load roles inside your call. Assuming you did
Role = mongoose.model('Role', RoleSchema)
you just need to run
Role.where('_id').in(user.roles).run( (err, res) ->
/* you have roles in res now */
)
this however is an asynchronous operation, which requires callback to be passed to your method, i.e.
UserSchema.method "isActiveSubscriber", (callback) ->
now = new Date()
if #roles.length is 0
return callback(false)
if #roles[0].type
/* roles are loaded,
maybe there is some other fancy way to do the check */
result = false
#roles.forEach (role) ->
if role.type is "SUBSCRIBER" and role.expiration > now
result = true
callback(result)
else
/* we have to load roles */
Role.where('_id').in(#roles).run( (err, res) =>
/* you have roles in res now */
/* you may want to populate the field,
note => sign in this function definition */
#roles = res
result = false
res.forEach (role) ->
if role.type is "SUBSCRIBER" and role.expiration > now
result = true
callback(result)
)
Now for a user user you can call it like this
user.isActiveSubscriber( (result) ->
/* do something with result */
)
The problem is that the operation is asynchronous and it forces additional callback nesting in your code (this will be pain if you want to check for roles for say 100 users, you will need some asynchronous calls handling library like async). So I advice populating this field whenever you load users and use the code you showed us. You can add static method (maybe you can even override default find method? I'm not sure about this though) for this.
b+c) The other option is to store roles as strings in your collection and hardcode possible roles in app. This will be simplier to implement, however adding/removing new role will be a pain in the $%^&*. The way you are doing this (i.e. via references) is fine and I think that the only thing you need is to populate the field whenever you search for users and everything will be fine.
The approach adopted is within the purview of how mongoose implements DBRef-like behavior using populate. However some things seem to have been missed out. Not sure if these details have already been take care and not shown for brevity. Will run through the complete set of steps:
RoleSchema = new Schema {
type: String
expiration: { type: Date, default : '0' }
}
UserSchema = new Schema {
email: { type: String, index: { unique: true }}
_roles : [{ type: Schema.ObjectId, ref: 'Role' }] // array of pointers for roles
}
Role = mongo.model('Role', 'RoleSchema');
User = mongo.model('User', 'UserSchema');
var life_sub = new Role({ type: 'LIFE_SUBSCRIBER', .... });
var daily_sub = new Role({ type: 'DAILY_SUBSCRIBER', .... });
var monthly_sub = new Role({ type: 'MONTHLY_SUBSCRIBER', .... });
var jane = new User({email: 'jane#ab.com', _roles: [daily_sub, monthly_sub]});
Add users as required. Next to find the specified user and the corresponding set of roles use:
User
.find({email:'jane#ab.com'})
.populate('_roles[]')
.run(function(err, user)
Now user is an array of pointers corresponding to the roles of the user specified. Iterate through the array and find each role for the user and the corresponding expiration date. Compare with Date.now() to arrive at which roles have expired for the particular user.
Hope this helps.

Resources