Commit 2392615cebbd2a0429d07b2f66811b00a72f3145
1 parent
0bbe393d
feature: Profile ist feature-complete
Showing
11 changed files
with
251 additions
and
89 deletions
pom.xml
| @@ -63,11 +63,11 @@ | @@ -63,11 +63,11 @@ | ||
| 63 | <artifactId>mysql-connector-java</artifactId> | 63 | <artifactId>mysql-connector-java</artifactId> |
| 64 | <version>${mysql-version}</version> | 64 | <version>${mysql-version}</version> |
| 65 | </dependency> | 65 | </dependency> |
| 66 | - <dependency> | 66 | + <!-- dependency> |
| 67 | <groupId>com.fasterxml.jackson.core</groupId> | 67 | <groupId>com.fasterxml.jackson.core</groupId> |
| 68 | <artifactId>jackson-databind</artifactId> | 68 | <artifactId>jackson-databind</artifactId> |
| 69 | <version>${jackson-version}</version> | 69 | <version>${jackson-version}</version> |
| 70 | - </dependency> | 70 | + </dependency --> |
| 71 | </dependencies> | 71 | </dependencies> |
| 72 | <build> | 72 | <build> |
| 73 | <finalName>swXercise</finalName> | 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,7 +16,7 @@ import net.ziemers.swxercise.db.BaseEntity; | ||
| 16 | public class GenericDao { | 16 | public class GenericDao { |
| 17 | 17 | ||
| 18 | @PersistenceContext | 18 | @PersistenceContext |
| 19 | - private EntityManager entityManager; | 19 | + protected EntityManager entityManager; |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Speichert die gegebene Entität. | 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
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 | import javax.validation.constraints.NotNull; | 4 | import javax.validation.constraints.NotNull; |
| 7 | 5 | ||
| 8 | import net.ziemers.swxercise.db.BaseEntity; | 6 | import net.ziemers.swxercise.db.BaseEntity; |
| 9 | 7 | ||
| 10 | @Entity | 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 | public class User extends BaseEntity { | 12 | public class User extends BaseEntity { |
| 12 | 13 | ||
| 13 | private String firstname; | 14 | private String firstname; |
| @@ -17,6 +18,8 @@ public class User extends BaseEntity { | @@ -17,6 +18,8 @@ public class User extends BaseEntity { | ||
| 17 | @NotNull | 18 | @NotNull |
| 18 | private Profile profile; | 19 | private Profile profile; |
| 19 | 20 | ||
| 21 | + private Address address; | ||
| 22 | + | ||
| 20 | public User() { | 23 | public User() { |
| 21 | super(); | 24 | super(); |
| 22 | } | 25 | } |
| @@ -56,4 +59,13 @@ public class User extends BaseEntity { | @@ -56,4 +59,13 @@ public class User extends BaseEntity { | ||
| 56 | return this; | 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
src/main/java/net/ziemers/swxercise/lg/user/service/UserService.java
| @@ -6,20 +6,23 @@ import javax.ejb.Stateless; | @@ -6,20 +6,23 @@ import javax.ejb.Stateless; | ||
| 6 | import javax.inject.Inject; | 6 | import javax.inject.Inject; |
| 7 | 7 | ||
| 8 | import net.ziemers.swxercise.db.dao.GenericDao; | 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 | import net.ziemers.swxercise.lg.user.dto.UserDto; | 12 | import net.ziemers.swxercise.lg.user.dto.UserDto; |
| 12 | 13 | ||
| 13 | @Stateless | 14 | @Stateless |
| 14 | public class UserService { | 15 | public class UserService { |
| 15 | 16 | ||
| 16 | @Inject | 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 | public Collection<User> findAllUsers() { | 24 | public Collection<User> findAllUsers() { |
| 22 | - return genericDao.findAll(User.class); | 25 | + return dao.findAll(User.class); |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 25 | public Long saveUser(final UserDto userDto) { | 28 | public Long saveUser(final UserDto userDto) { |
| @@ -29,7 +32,7 @@ public class UserService { | @@ -29,7 +32,7 @@ public class UserService { | ||
| 29 | final User user = new User(userDto.getFirstname(), userDto.getLastname()) | 32 | final User user = new User(userDto.getFirstname(), userDto.getLastname()) |
| 30 | .withProfile(profile); | 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,8 +7,7 @@ import javax.inject.Inject; | ||
| 7 | import javax.ws.rs.*; | 7 | import javax.ws.rs.*; |
| 8 | import javax.ws.rs.core.MediaType; | 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 | import net.ziemers.swxercise.lg.user.dto.UserDto; | 11 | import net.ziemers.swxercise.lg.user.dto.UserDto; |
| 13 | import net.ziemers.swxercise.lg.user.service.UserService; | 12 | import net.ziemers.swxercise.lg.user.service.UserService; |
| 14 | 13 | ||
| @@ -54,7 +53,6 @@ public class UserViewController { | @@ -54,7 +53,6 @@ public class UserViewController { | ||
| 54 | @Produces({MediaType.TEXT_PLAIN}) | 53 | @Produces({MediaType.TEXT_PLAIN}) |
| 55 | public String postUser(UserDto userDto) throws Exception { | 54 | public String postUser(UserDto userDto) throws Exception { |
| 56 | userService.saveUser(userDto); | 55 | userService.saveUser(userDto); |
| 57 | - | ||
| 58 | return "Ok"; | 56 | return "Ok"; |
| 59 | } | 57 | } |
| 60 | 58 |