Commit 2392615cebbd2a0429d07b2f66811b00a72f3145

Authored by Thomas Ziemer
1 parent 0bbe393d

feature: Profile ist feature-complete

... ... @@ -63,11 +63,11 @@
63 63 <artifactId>mysql-connector-java</artifactId>
64 64 <version>${mysql-version}</version>
65 65 </dependency>
66   - <dependency>
  66 + <!-- dependency>
67 67 <groupId>com.fasterxml.jackson.core</groupId>
68 68 <artifactId>jackson-databind</artifactId>
69 69 <version>${jackson-version}</version>
70   - </dependency>
  70 + </dependency -->
71 71 </dependencies>
72 72 <build>
73 73 <finalName>swXercise</finalName>
... ...
src/main/java/net/ziemers/swxercise/db/dao/GenericDao.java
... ... @@ -16,7 +16,7 @@ import net.ziemers.swxercise.db.BaseEntity;
16 16 public class GenericDao {
17 17  
18 18 @PersistenceContext
19   - private EntityManager entityManager;
  19 + protected EntityManager entityManager;
20 20  
21 21 /**
22 22 * Speichert die gegebene Entität.
... ...
src/main/java/net/ziemers/swxercise/db/dao/user/UserDao.java 0 → 100644
  1 +package net.ziemers.swxercise.db.dao.user;
  2 +
  3 +import net.ziemers.swxercise.db.dao.GenericDao;
  4 +import net.ziemers.swxercise.lg.model.user.User;
  5 +
  6 +import javax.ejb.Stateless;
  7 +
  8 +@Stateless
  9 +public class UserDao extends GenericDao {
  10 +
  11 + public User findById(final Long id) {
  12 + User user = null;
  13 +
  14 + try {
  15 + // ermittelt den ersten Datensatz mit der gesuchten Id, auch wenn er sich nicht im Persistence Context befindet
  16 + user = (User) entityManager.createNamedQuery("User.findById").setParameter("id", id).getSingleResult();
  17 + } catch(Exception e) {
  18 + /* nix */
  19 + }
  20 + return user;
  21 + }
  22 +
  23 +}
... ...
src/main/java/net/ziemers/swxercise/lg/user/Address.java renamed to src/main/java/net/ziemers/swxercise/lg/model/user/Address.java
1   -package net.ziemers.swxercise.lg.user;
  1 +package net.ziemers.swxercise.lg.model.user;
2 2  
3 3 import javax.persistence.Entity;
4 4  
... ...
src/main/java/net/ziemers/swxercise/lg/model/user/Profile.java 0 → 100644
  1 +package net.ziemers.swxercise.lg.model.user;
  2 +
  3 +import javax.persistence.Entity;
  4 +import javax.persistence.EnumType;
  5 +import javax.persistence.Enumerated;
  6 +import javax.validation.constraints.NotNull;
  7 +import javax.xml.bind.DatatypeConverter;
  8 +
  9 +import net.ziemers.swxercise.db.BaseEntity;
  10 +import net.ziemers.swxercise.lg.user.enums.PasswordHashAlgorithm;
  11 +
  12 +import java.security.MessageDigest;
  13 +import java.security.SecureRandom;
  14 +import java.util.Random;
  15 +
  16 +@Entity
  17 +public class Profile extends BaseEntity {
  18 +
  19 + @NotNull
  20 + private String username;
  21 +
  22 + @NotNull
  23 + private String passwordHash;
  24 +
  25 + @NotNull
  26 + private PasswordHashAlgorithm hashAlgorithm = PasswordHashAlgorithm.SHA512;
  27 +
  28 + @NotNull
  29 + private String salt;
  30 +
  31 + private String mailaddress;
  32 +
  33 + /*
  34 + * *****************************************************************************************************************************
  35 + * Konstruktoren
  36 + * *****************************************************************************************************************************
  37 + */
  38 +
  39 + public Profile() {
  40 + super();
  41 +
  42 + setSalt(this.generateSalt());
  43 + }
  44 +
  45 + public Profile(final String username, final String password) {
  46 + this();
  47 +
  48 + setUsername(username);
  49 + setPasswordHash(password);
  50 + }
  51 +
  52 + /*
  53 + * *****************************************************************************************************************************
  54 + * Methoden
  55 + * *****************************************************************************************************************************
  56 + */
  57 +
  58 + private byte[] base64ToByte(final String str) {
  59 + byte[] bytes;
  60 + // Java 6 ships the javax.xml.bind.DatatypeConverter this class provides two static methods that support the same decoding &
  61 + // encoding: parseBase64Binary() and printBase64Binary(). Use this and you will not need an extra library, like Apache
  62 + // Commons Codec.
  63 + // BASE64Decoder decoder = new BASE64Decoder();
  64 +
  65 + // try {
  66 + // bytes = decoder.decodeBuffer(str);
  67 + bytes = DatatypeConverter.parseBase64Binary(str);
  68 + // } catch(IOException e) {
  69 + // e.printStackTrace();
  70 + // }
  71 + return bytes;
  72 + }
  73 + private String byteToBase64(final byte[] bytes) {
  74 + // Java 6 ships the javax.xml.bind.DatatypeConverter this class provides two static methods that support the same decoding &
  75 + // encoding: parseBase64Binary() and printBase64Binary(). Use this and you will not need an extra library, like Apache
  76 + // Commons Codec.
  77 + // BASE64Encoder encoder = new BASE64Encoder();
  78 +
  79 + // String str = encoder.encode(bytes);
  80 + String str = DatatypeConverter.printBase64Binary(bytes);
  81 + return str;
  82 + }
  83 +
  84 + private String cryptString(String string) {
  85 + final int HASH_ITERATION_COUNT = 5;
  86 +
  87 + String hashedString = "";
  88 +
  89 + // vor dem Verschlüsseln den benutzerspezifischen Salt an das Kennwort anhängen
  90 + string += this.getSalt();
  91 +
  92 + // Quelle: http://codeschnipsel.wordpress.com/2008/11/13/passwort-hash-in-java/
  93 + MessageDigest md;
  94 + try {
  95 + md = MessageDigest.getInstance(getHashAlgorithm().getAlgorithm());
  96 + md.reset();
  97 + md.update(base64ToByte(this.getSalt()));
  98 +
  99 + byte[] bytes = md.digest(string.getBytes("UTF-8"));
  100 + for(int i = 0; i < HASH_ITERATION_COUNT; i++) {
  101 + md.reset();
  102 + bytes = md.digest(bytes);
  103 + }
  104 + hashedString = byteToBase64(bytes);
  105 + } catch(Exception e) {
  106 + e.printStackTrace();
  107 + }
  108 + return hashedString;
  109 + }
  110 +
  111 + private String generateSalt() {
  112 + final int HASH_LENGTH = 20; // Ferguson & Schneier overcautiously recommend 32
  113 +
  114 + // Quelle: http://www.javamex.com/tutorials/cryptography/pbe_salt.shtml
  115 + Random random = new SecureRandom();
  116 + byte[] salt = new byte[HASH_LENGTH];
  117 +
  118 + random.nextBytes(salt);
  119 + return byteToBase64(salt);
  120 + }
  121 +
  122 + /*
  123 + * *****************************************************************************************************************************
  124 + * Getters und Setters
  125 + * *****************************************************************************************************************************
  126 + */
  127 +
  128 + public String getUsername() {
  129 + return username;
  130 + }
  131 +
  132 + public void setUsername(String username) {
  133 + this.username = username;
  134 + }
  135 +
  136 + // da die Methode "private" ist, wird der Kennwort-Hashwert niemals per REST in einem JSON-Objekt übermittelt
  137 + private String getPasswordHash() {
  138 + return passwordHash;
  139 + }
  140 +
  141 + private void setPasswordHash(String password) {
  142 + // das Klartextkennwort wird niemals gespeichert!
  143 + this.passwordHash = cryptString(password);
  144 + }
  145 +
  146 + public boolean isValidPassword(final String password) {
  147 + return getPasswordHash().equals(cryptString(password));
  148 + }
  149 +
  150 + @Enumerated(EnumType.STRING)
  151 + private PasswordHashAlgorithm getHashAlgorithm() {
  152 + return hashAlgorithm;
  153 + }
  154 +
  155 + private void setHashAlgorithm(PasswordHashAlgorithm hashAlgorithm) {
  156 + this.hashAlgorithm = hashAlgorithm;
  157 + }
  158 +
  159 + // da die Methode "private" ist, wird der Kennwort-Saltwert niemals per REST in einem JSON-Objekt übermittelt
  160 + private String getSalt() {
  161 + return salt;
  162 + }
  163 +
  164 + private void setSalt(String salt) {
  165 + this.salt = salt;
  166 + }
  167 +
  168 + public String getMailaddress() {
  169 + return mailaddress;
  170 + }
  171 +
  172 + public void setMailaddress(String mailaddress) {
  173 + this.mailaddress = mailaddress;
  174 + }
  175 +
  176 +}
... ...
src/main/java/net/ziemers/swxercise/lg/user/User.java renamed to src/main/java/net/ziemers/swxercise/lg/model/user/User.java
1   -package net.ziemers.swxercise.lg.user;
  1 +package net.ziemers.swxercise.lg.model.user;
2 2  
3   -import javax.persistence.CascadeType;
4   -import javax.persistence.Entity;
5   -import javax.persistence.OneToOne;
  3 +import javax.persistence.*;
6 4 import javax.validation.constraints.NotNull;
7 5  
8 6 import net.ziemers.swxercise.db.BaseEntity;
9 7  
10 8 @Entity
  9 +@NamedQueries({
  10 + @NamedQuery(name = "User.findById", query = "SELECT u FROM User u WHERE u.id = :id"),
  11 + @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")})
11 12 public class User extends BaseEntity {
12 13  
13 14 private String firstname;
... ... @@ -17,6 +18,8 @@ public class User extends BaseEntity {
17 18 @NotNull
18 19 private Profile profile;
19 20  
  21 + private Address address;
  22 +
20 23 public User() {
21 24 super();
22 25 }
... ... @@ -56,4 +59,13 @@ public class User extends BaseEntity {
56 59 return this;
57 60 }
58 61  
  62 + @OneToOne(cascade = {CascadeType.ALL})
  63 + public Address getAddress() {
  64 + return address;
  65 + }
  66 +
  67 + public void setAddress(Address address) {
  68 + this.address = address;
  69 + }
  70 +
59 71 }
... ...
src/main/java/net/ziemers/swxercise/lg/user/Profile.java deleted 100644 → 0
1   -package net.ziemers.swxercise.lg.user;
2   -
3   -import javax.persistence.Entity;
4   -import javax.validation.constraints.NotNull;
5   -
6   -import net.ziemers.swxercise.db.BaseEntity;
7   -import net.ziemers.swxercise.lg.user.enums.PasswordHashType;
8   -
9   -@Entity
10   -public class Profile extends BaseEntity {
11   -
12   - @NotNull
13   - private String username;
14   -
15   - @NotNull
16   - private String passwordHash;
17   -
18   - @NotNull
19   - private PasswordHashType passwordHashType = PasswordHashType.Unknown;
20   -
21   - private String mailaddress;
22   -
23   - public Profile() {
24   - super();
25   - }
26   -
27   - public Profile(final String username, final String password) {
28   - this.username = username;
29   - // TODO Kennwort verhashen
30   - this.passwordHash = password;
31   - }
32   -
33   - public String getUsername() {
34   - return username;
35   - }
36   -
37   - public void setUsername(String username) {
38   - this.username = username;
39   - }
40   -
41   - private String getPasswordHash() {
42   - return passwordHash;
43   - }
44   -
45   - private void setPasswordHash(String passwordHash) {
46   - this.passwordHash = passwordHash;
47   - }
48   -
49   - private PasswordHashType getPasswordHashType() {
50   - return passwordHashType;
51   - }
52   -
53   - private void setPasswordHashType(PasswordHashType passwordHashType) {
54   - this.passwordHashType = passwordHashType;
55   - }
56   -
57   - public String getMailaddress() {
58   - return mailaddress;
59   - }
60   -
61   - public void setMailaddress(String mailaddress) {
62   - this.mailaddress = mailaddress;
63   - }
64   -
65   -}
src/main/java/net/ziemers/swxercise/lg/user/enums/PasswordHashAlgorithm.java 0 → 100644
  1 +package net.ziemers.swxercise.lg.user.enums;
  2 +
  3 +public enum PasswordHashAlgorithm {
  4 +
  5 + SHA512("SHA-512"),
  6 + ;
  7 +
  8 + private String algorithm;
  9 +
  10 + PasswordHashAlgorithm(final String algorithm) {
  11 + setAlgorithm(algorithm);
  12 + }
  13 +
  14 + public String getAlgorithm() {
  15 + return algorithm;
  16 + }
  17 +
  18 + private void setAlgorithm(String algorithm) {
  19 + this.algorithm = algorithm;
  20 + }
  21 +
  22 +}
... ...
src/main/java/net/ziemers/swxercise/lg/user/enums/PasswordHashType.java deleted 100644 → 0
1   -package net.ziemers.swxercise.lg.user.enums;
2   -
3   -public enum PasswordHashType {
4   -
5   - Unknown,
6   - ;
7   -}
src/main/java/net/ziemers/swxercise/lg/user/service/UserService.java
... ... @@ -6,20 +6,23 @@ import javax.ejb.Stateless;
6 6 import javax.inject.Inject;
7 7  
8 8 import net.ziemers.swxercise.db.dao.GenericDao;
9   -import net.ziemers.swxercise.lg.user.Profile;
10   -import net.ziemers.swxercise.lg.user.User;
  9 +import net.ziemers.swxercise.db.dao.user.UserDao;
  10 +import net.ziemers.swxercise.lg.model.user.Profile;
  11 +import net.ziemers.swxercise.lg.model.user.User;
11 12 import net.ziemers.swxercise.lg.user.dto.UserDto;
12 13  
13 14 @Stateless
14 15 public class UserService {
15 16  
16 17 @Inject
17   - private GenericDao genericDao;
  18 + private UserDao dao;
18 19  
19   - public User findUser(final Long id) { return genericDao.findById(User.class, id); }
  20 + public User findUser(final Long id) {
  21 + return dao.findById(id);
  22 + }
20 23  
21 24 public Collection<User> findAllUsers() {
22   - return genericDao.findAll(User.class);
  25 + return dao.findAll(User.class);
23 26 }
24 27  
25 28 public Long saveUser(final UserDto userDto) {
... ... @@ -29,7 +32,7 @@ public class UserService {
29 32 final User user = new User(userDto.getFirstname(), userDto.getLastname())
30 33 .withProfile(profile);
31 34  
32   - return genericDao.save(user);
  35 + return dao.save(user);
33 36 }
34 37  
35 38 }
... ...
src/main/java/net/ziemers/swxercise/ui/UserViewController.java
... ... @@ -7,8 +7,7 @@ import javax.inject.Inject;
7 7 import javax.ws.rs.*;
8 8 import javax.ws.rs.core.MediaType;
9 9  
10   -import net.ziemers.swxercise.lg.user.Profile;
11   -import net.ziemers.swxercise.lg.user.User;
  10 +import net.ziemers.swxercise.lg.model.user.User;
12 11 import net.ziemers.swxercise.lg.user.dto.UserDto;
13 12 import net.ziemers.swxercise.lg.user.service.UserService;
14 13  
... ... @@ -54,7 +53,6 @@ public class UserViewController {
54 53 @Produces({MediaType.TEXT_PLAIN})
55 54 public String postUser(UserDto userDto) throws Exception {
56 55 userService.saveUser(userDto);
57   -
58 56 return "Ok";
59 57 }
60 58  
... ...