-
Notifications
You must be signed in to change notification settings - Fork 118
/
Copy pathUserService.cs
159 lines (127 loc) · 5.3 KB
/
UserService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using System;
using System.Collections.Generic;
using System.Linq;
using WebApi.Entities;
using WebApi.Helpers;
namespace WebApi.Services
{
public interface IUserService
{
User Authenticate(string dni, string password);
IEnumerable<User> GetAll();
User GetById(int id);
User Create(User user, string password);
void Update(User user, string password = null);
void Delete(int id);
}
public class UserService : IUserService
{
private DataContext _context;
public UserService(DataContext context)
{
_context = context;
}
public User Authenticate(string dni, string password)
{
if (string.IsNullOrEmpty(dni) || string.IsNullOrEmpty(password))
return null;
var user = _context.Users.SingleOrDefault(x => x.dni == dni);
// check if dni exists
if (user == null)
return null;
// check if password is correct
if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
return null;
// authentication successful
return user;
}
public IEnumerable<User> GetAll()
{
return _context.Users;
}
public User GetById(int id)
{
return _context.Users.Find(id);
}
public User Create(User user, string password)
{
// validation
if (string.IsNullOrWhiteSpace(password))
throw new AppException("Password is required");
if (_context.Users.Any(x => x.dni == user.dni))
throw new AppException("dni \"" + user.dni + "\" is already taken");
byte[] passwordHash, passwordSalt;
CreatePasswordHash(password, out passwordHash, out passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
_context.Users.Add(user);
_context.SaveChanges();
return user;
}
public void Update(User userParam, string password = null)
{
var user = _context.Users.Find(userParam.Id);
if (user == null)
throw new AppException("User not found");
// update dni if it has changed
if (!string.IsNullOrWhiteSpace(userParam.dni) && userParam.dni != user.dni)
{
// throw error if the new dni is already taken
if (_context.Users.Any(x => x.dni == userParam.dni))
throw new AppException("dni " + userParam.dni + " is already taken");
user.dni = userParam.dni;
}
// update user properties if provided
if (!string.IsNullOrWhiteSpace(userParam.FirstName))
user.FirstName = userParam.FirstName;
if (!string.IsNullOrWhiteSpace(userParam.LastName))
user.LastName = userParam.LastName;
// update password if provided
if (!string.IsNullOrWhiteSpace(password))
{
byte[] passwordHash, passwordSalt;
CreatePasswordHash(password, out passwordHash, out passwordSalt);
user.PasswordHash = passwordHash;
user.PasswordSalt = passwordSalt;
}
_context.Users.Update(user);
_context.SaveChanges();
}
public void Delete(int id)
{
var user = _context.Users.Find(id);
if (user != null)
{
_context.Users.Remove(user);
_context.SaveChanges();
}
}
// private helper methods
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
if (password == null) throw new ArgumentNullException("password");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
{
if (password == null) throw new ArgumentNullException("password");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", "passwordHash");
if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", "passwordHash");
using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
{
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
for (int i = 0; i < computedHash.Length; i++)
{
if (computedHash[i] != storedHash[i]) return false;
}
}
return true;
}
}
}