I'm trying to connect my asp .net identity classes to custom sql database. I have defined the mapping in the OnModelBuilder method as follows:
var user = modelBuilder.Entity<IdentityUser>().HasKey(u => u.Id).ToTable("User");
user.Property(iu => iu.Id).HasColumnName("Id");
user.Property(iu => iu.UserName).HasColumnName("UserName");
user.Property(iu => iu.PasswordHash).HasColumnName("PasswordHash");
user.Property(iu => iu.SecurityStamp).HasColumnName("SecurityStamp");
user.Property(iu => iu.EmailConfirmed).HasColumnName("EmailConfirmed");
user.Property(iu => iu.LockoutEnabled).HasColumnName("LockoutEnabled");
user.Property(iu => iu.LockoutEndDateUtc).HasColumnName("LockoutEndDateUtc");
user.Property(iu => iu.PhoneNumber).HasColumnName("PhoneNumber");
user.Property(iu => iu.PhoneNumberConfirmed).HasColumnName("PhoneNumberConfirmed");
user.Property(iu => iu.TwoFactorEnabled).HasColumnName("TwoFactorEnabled");
user.Property(au => au.AccessFailedCount).HasColumnName("AccessFailedCount");
user.Property(au => au.TwoFactorEnabled).HasColumnName("TwoFactorEnabled");
//--?--
user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
user.HasMany(u => u.Claims).WithRequired().HasForeignKey(ur => ur.UserId);
user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ur => ur.UserId);
user.Property(u => u.UserName).IsRequired();
//--
var applicationUser = modelBuilder.Entity<ApplicationUser>().HasKey(au => au.Id).ToTable("User");
applicationUser.Property(au => au.Id).HasColumnName("Id");
applicationUser.Property(au => au.UserName).HasColumnName("UserName");
applicationUser.Property(au => au.Email).HasColumnName("Email");
applicationUser.Property(au => au.PasswordHash).HasColumnName("PasswordHash");
applicationUser.Property(au => au.SecurityStamp).HasColumnName("SecurityStamp");
applicationUser.Property(au => au.EmailConfirmed).HasColumnName("EmailConfirmed");
applicationUser.Property(au => au.ActivationToken).HasColumnName("ActivationToken");
applicationUser.Property(au => au.FirstName).HasColumnName("FirstName");
applicationUser.Property(au => au.LastName).HasColumnName("LastName");
applicationUser.Property(au => au.LockoutEnabled).HasColumnName("LockoutEnabled");
applicationUser.Property(au => au.LockoutEndDateUtc).HasColumnName("LockoutEndDateUtc");
applicationUser.Property(au => au.PhoneNumber).HasColumnName("PhoneNumber");
applicationUser.Property(au => au.PhoneNumberConfirmed).HasColumnName("PhoneNumberConfirmed");
applicationUser.Property(au => au.AccessFailedCount).HasColumnName("AccessFailedCount");
applicationUser.Property(au => au.Discriminator1).HasColumnName("Discriminator1");
applicationUser.Property(au => au.TwoFactorEnabled).HasColumnName("TwoFactorEnabled");
applicationUser.Property(au => au.IdentityRole_Id).HasColumnName("IdentityRole_Id");
//set the primary key for this class. Use .HasMaxLength(40) to specify max length on property
var role = modelBuilder.Entity<IdentityRole>().HasKey(ir => ir.Id).ToTable("Role");
role.Property(ir => ir.Id).HasColumnName("Id");
role.Property(ir => ir.Name).HasColumnName("Name");
var applicationRole = modelBuilder.Entity<ApplicationRole>().HasKey(ar => ar.Id).ToTable("Role");
applicationRole.Property(ir => ir.Id).HasColumnName("Id");
applicationRole.Property(ir => ir.Name).HasColumnName("Name");
//applicationRole.Property(ir => ir.Description).HasColumnName("Description");
var userRole = modelBuilder.Entity<IdentityUserRole>().HasKey(iur => new{iur.UserId, iur.RoleId}).ToTable("UserRole");
userRole.Property(ur => ur.RoleId).HasColumnName("RoleId");
userRole.Property(ur => ur.UserId).HasColumnName("UserId");
var claim = modelBuilder.Entity<IdentityUserClaim>().HasKey(iuc => iuc.Id).ToTable("UserClaim");
claim.Property(iuc => iuc.Id).HasColumnName("Id");
claim.Property(iuc => iuc.ClaimType).HasColumnName("ClaimType");
claim.Property(iuc => iuc.ClaimValue).HasColumnName("ClaimValue");
claim.Property(iuc => iuc.UserId).HasColumnName("UserId");
var login = modelBuilder.Entity<IdentityUserLogin>().HasKey(iul => new { iul.UserId, iul.LoginProvider, iul.ProviderKey }).ToTable("UserLogin");
login.Property(iul => iul.UserId).HasColumnName("UserId");
login.Property(iul => iul.LoginProvider).HasColumnName("LoginProvider");
login.Property(iul => iul.ProviderKey).HasColumnName("ProviderKey");
However when I run, its giving error saying
Invalid column name 'Discriminator'.
Invalid column name 'IdentityRole_Id'.
Invalid column name 'Discriminator'.
Invalid column name 'Discriminator'.
If I understand correctly, its looking for those column names inside my custom User table, even though they are not defined in IdentityUser or my derived ApplicationUser.
I'm not sure where the error lies, my OnModelCreating method?
This is not an ASP.NET identity issue. You are mapping both, base class and derived class,IdentityUser and ApplicationUser respectively to the same table "User", this is known as "table per class hierarchy", so EF adds Descriminator column accordingly.
In this case you should mapp only ApplicationUser class.
The same applies to IdentityRole and ApplicationRole, etc.
Related
I have this code to register a user:
public function store(Request $request)
{
$request->validate([
'first_name' => 'required|string|min:2|max:255',
'last_name' => 'required|string|min:2|max:255',
'mobile_phone_number' =>
required|string|min:11|max:15|unique:users|phone:NG,mobile',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|confirmed|min:12',
'terms' => 'required',
]);
Auth::login($user = User::create([
'first_name' => $request->first_name,
'last_name' => $request->last_name,
'mobile_phone_number' => $request->mobile_phone_number,
'email' => $request->email,
'password' => Hash::make($request->password),
]));
$request->user()->markTermsAsAccepted();
event(new Registered($user));
return redirect('/verify-email');
}
My validation rules for mobile phone number includes this: 'phone:NG,mobile' to specify that only numbers from the specific locale are acceptable.
This happens to work.
I now have this code in another project:
public function create(array $input)
{
Validator::make($input, [
'first_name' => ['required', 'string', 'max:255'],
'last_name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'mobile_phone_number' => ['required', 'string', 'min:11', 'max:15', 'unique:users', 'phone:NG,mobile'],
'password' => $this->passwordRules(),
'terms' => Jetstream::hasTermsAndPrivacyPolicyFeature() ? ['required', 'accepted'] : '',
])->validate();
return User::create([
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'email' => $input['email'],
'mobile_phone_number' => $input['mobile_phone_number'],
'password' => Hash::make($input['password']),
]);
}
But this doesn't work. I get this error: BadMethodCallException Method Illuminate\Validation\Validator::validatePhone does not exist.
Yes, there's no 'Phone' validation rule in the Laravel docs and I'm wondering how come the first code works and the second doesn't.
As I'm having a single DTO, we use DTOs for GET, PUT and POST http method in our Web API.
To make simple we have ActivityDO:
public ActivityDTO
{
public int Id;
public string Name;
public string CategoryName;
public DateTime DateCreated;
public DateTime DateModified;
}
The challenge is when you only have a single DTO for handling multiple conditions i.e. post/get/put method, the mapping as follow:
private MapperConfiguration configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<ActivityDTO, Activity>()
.ForMember(dst => dst.UserId, opt => opt.MapFrom(src => HttpContext.Current.User.Identity.GetUserId()))
.ForMember(dst => dst.CategoryId, opt => opt.MapFrom(src => GetCategoryId(HttpContext.Current.User.Identity.GetUserId(), src.CategoryName)))
.ForMember(dst => dst.DateCreated, opt => opt.MapFrom(src => DateTime.UtcNow))
.ForMember(dst => dst.DateModified, opt => opt.MapFrom(src => DateTime.UtcNow));
});
I want to IGNORE the mapping for DateCreated if we do the update and we can do the condition if the id <= 0, the rest is allowed to mapping for DateCreated.
Is this possible? Would rather to have a seperate DTOs between GET/POST (Add) VS PUT (Update)? Is there any better solution to handle this DateCreated VS DateModified thingy?
I'm appreciated your feedback/comment.
This is the way to add conditions.
Is that what you are looking for?
private MapperConfiguration configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<ActivityDTO, Activity>()
.ForMember(dst => dst.UserId, opt => opt.MapFrom(src => HttpContext.Current.User.Identity.GetUserId()))
.ForMember(dst => dst.CategoryId, opt => opt.MapFrom(src => GetCategoryId(HttpContext.Current.User.Identity.GetUserId(), src.CategoryName)))
.ForMember(dst => dst.DateCreated, opt => opt.MapFrom(src => src.Condition(src.DateCreated != null)))
.ForMember(dst => dst.DateModified, opt => opt.MapFrom(src => DateTime.UtcNow));
});
I used src.DateCreated != null but you can specify any condition using the src.Condition() and the variable will only be mapped when the condition is met.
Also
You can use AutoMapper's PreCondition
var configuration = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForMember(dest => dest.baz, opt => {
opt.PreCondition(src => (src.baz >= 0));
opt.MapFrom(src => {
});
});
});
I do not use the "Form Service Provider" and manually output the CSRF token to my twig login form:
$csrf_token = $app['csrf.token_manager']->getToken('token_id'); //'TOKEN'
And in the login.html.twig:
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}">
The manual (https://silex.symfony.com/doc/2.0/providers/csrf.html) says, that it's possible to check the token like this:
$app['csrf.token_manager']->isTokenValid(new CsrfToken('token_id', 'TOKEN'));
But the whole login process is handled by the security component. How do I add the CSRF check to it?
This is my firewall setup:
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/user/login$',
),
'secured_area' => array(
'pattern' => '^.*$',
'anonymous' => false,
'remember_me' => array(),
'form' => array(
'login_path' => '/user/login',
'check_path' => '/user/login_check',
),
'logout' => array(
'logout_path' => '/user/logout',
'invalidate_session' => true
),
'users' => function () use ($app) {
return new UserProvider($app['db']);
},
));
And the Login controller:
$app->get('/user/login', function(Request $request) use ($app) {
$csrf_token = $app['csrf.token_manager']->getToken('token_id'); //'TOKEN'
return $app['twig']->render('login.html.twig', array(
'csrf_token' => $csrf_token,
));
});
Try to add csrf options to security config:
$app['security.firewalls'] = array(
....
'form' => array(
'login_path' => '/user/login',
'check_path' => '/user/login_check',
'with_csrf' => true,
'csrf_parameter' => '_csrf_token', // form field name
'csrf_token_id' => 'token_id'
),
....
I use htmlpurifier as a filter for forms. But it does not work after migrating zf2->zf3. "A plugin by the name "htmlpurifier" was not found in the plugin manager Zend\Filter\FilterPluginManager". Though in module config htmlpurifier is present.
class PostFieldset extends Fieldset implements InputFilterProviderInterface:
public function __construct(PostInterface $post, HydratorInterface $hydrator, $name = "post", $options = array())
parent::__construct($name, $options);
$this->setHydrator($hydrator);
$this->setObject($post);
...
$this->add(array(
'type' => 'textarea',
'name' => 'text',
'attributes'=>array(
'class' => 'form-control',
'required' => 'required',
'rows' => '3',
),
'options' => array(
'label' => 'The text'
)
));
public function getInputFilterSpecification() :
return array(
'text' => array(
'required' => true,
'filters'=>array(
array(
'name' => 'htmlpurifier'
),
),
'validators' => array(
array(
'name'=>'StringLength',
'options'=>array(
'encoding'=>'UTF-8',
'min'=>1,
'max'=>250000,
)
)
)
),
module config in zenddevelopertools:
'filters' =>
array (size=2)
'factories' =>
array (size=1)
'Soflomo\Purifier\PurifierFilter' => string 'Soflomo\Purifier\Factory\PurifierFilterFactory' (length=46)
'aliases' =>
array (size=1)
'htmlpurifier' => string 'Soflomo\Purifier\PurifierFilter' (length=31)
https://bitbucket.org/mad-max/blog-note3
Deleting vendor folder and installing again have helped.
I am using cakephp 2.3 and required to redirect user after the excel sheet is downloaded successfully. I am using the cake $this->response->type for setting the view as excel sheet generator.
public function admin_export_excel($task_lists = array()) {
$task_ids = $task_lists;
$global_task_array = array();
$this->loadModel('Task');
//-> For each task-id run the loop for fetching the related data to generate the report.
foreach ($task_ids as $index => $task_id) {
//-> Check if the task exists with the specified id.
$this->Task->id = $task_id;
if (!$this->Task->exists())
throw new NotFoundException('Task not found.');
//-> Now check if the logged user is the owner of the specified task.
$task_count = $this->Task->find('count', array('conditions' => array('Task.id' => $task_id,
'Task.user_id' => $this->Auth->user('id'))));
if ($task_count == 0)
throw new NotFoundException('Task not accessable.');
$task_data = $this->Task->find('first', array(
'conditions' => array(
'Task.id' => $task_id
),
'contain' => array(
'TaskForm' => array(
'fields' => array('TaskForm.id', 'TaskForm.reference_table')
),
'Project' => array(
'fields' => array('Project.id', 'Project.project_name')
),
'User' => array(
'fields' => array('User.id', 'User.company_name')
),
'Timezone' => array(
'fields' => array('Timezone.id', 'Timezone.name')
)
)
)
);
// debug($task_data);
$global_task_array[$index] = $task_data;
//-> End of Custom else conditions
unset($task_data);
}
$this->set('global_task_array', $global_task_array);
$this->response->type(array('xls' => 'application/vnd.ms-excel'));
$this->response->type('xls');
$this->render('admin_export_excel');
}
and my view file is
$this->PhpExcel->createWorksheet();
$this->PhpExcel->setDefaultFont('Calibri', 13);
$default = array(
array('label' => __('Task Id'), 'width' => 'auto'),
array('label' => __('Unique Code'), 'width' => 'auto'),
array('label' => __('Site Name'), 'width' => 'auto'),
array('label' => __('Area'), 'width' => 'auto'),
array('label' => __('Location'), 'width' => 'auto'),
array('label' => __('Sub Location'), 'width' => 'auto'),
array('label' => __('About Task'), 'width' => 'auto')
);
$this->PhpExcel->addTableHeader($default, array('name' => 'Cambria', 'bold' => true));
$this->PhpExcel->setDefaultFont('Calibri', 12);
foreach ($global_task_array as $index => $raw) {
$data = array(
$raw['Task']['id'],
$raw['Task']['unique_code'],
$raw['Task']['site_name'],
$raw['Task']['area'],
$raw['Task']['location'],
$raw['Task']['sub_location'],
$raw['Task']['about_task']
);
$this->PhpExcel->addTableRow($data);
}
$this->PhpExcel->addTableFooter();
$this->PhpExcel->output('Task-' . date('d-m-Y') . '.xlsx');
I have tried to use the cake afterFilter method for to redirect the user to other action after the excel sheet is generated but its not working.
public function afterFilter(){
parent::afterFilter();
if($this->request->params['action'] == 'admin_export_excel'){
$this->redirect(array('controller' => 'tasks', 'action' => 'index','admin' => true));
}
}
Any help will be appreciated. Thanks