Steffen Nitschke
4 years ago
5 changed files with 484 additions and 0 deletions
-
152fh.fd.ci.server/src/main/java/de/fd/fh/server/access/AccessService.java
-
62fh.fd.ci.server/src/main/java/de/fd/fh/server/access/AccessToken.java
-
13fh.fd.ci.server/src/main/java/de/fd/fh/server/access/events/AccountCreatedEvent.java
-
12fh.fd.ci.server/src/main/java/de/fd/fh/server/access/events/AccountDeletedEvent.java
-
245fh.fd.ci.server/src/test/java/de/fd/fh/server/access/AccessServiceTest.java
@ -0,0 +1,152 @@ |
|||
package de.fd.fh.server.access; |
|||
|
|||
import de.fd.fh.server.access.events.AccountCreatedEvent; |
|||
import de.fd.fh.server.access.events.AccountDeletedEvent; |
|||
import de.fd.fh.server.user.UserId; |
|||
import de.fd.fh.shared.network.messages.LoginRequest; |
|||
import de.fd.fh.shared.network.messages.RegistrateRequest; |
|||
import lombok.RequiredArgsConstructor; |
|||
import org.bson.types.ObjectId; |
|||
|
|||
import java.util.Base64; |
|||
import java.util.Observable; |
|||
|
|||
import static spark.Spark.halt; |
|||
|
|||
@RequiredArgsConstructor |
|||
public class AccessService extends Observable |
|||
{ |
|||
private final AccessRepository accessRepository; |
|||
|
|||
public AccessToken before(final String path, final String token) { |
|||
System.out.println("Pfad: " + path); |
|||
if (!(path.equals("/accounts/login") |
|||
|| path.equals("/accounts/registrate") |
|||
)) |
|||
{ |
|||
final AccessToken accessToken = authenticate(token); |
|||
|
|||
if (accessToken == null) |
|||
{ |
|||
halt(401); |
|||
} |
|||
return accessToken; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
private AccessToken authenticate(final String bearerToken) |
|||
{ |
|||
return accessRepository.findByToken(bearerToken.substring("Bearer ".length())).getToken(); |
|||
} |
|||
|
|||
public boolean createPlayer(RegistrateRequest message) |
|||
{ |
|||
System.out.println("createPlayer: " + message); |
|||
|
|||
if (userNameDoesNotExist(message.getUserName())) |
|||
{ |
|||
System.out.println("Name does exist."); |
|||
return false; |
|||
} |
|||
|
|||
final Access access = new Access( |
|||
new ObjectId().toHexString(), |
|||
message.getUserName(), |
|||
message.getPassword(), |
|||
UserId.random(), |
|||
null, |
|||
Role.USER |
|||
); |
|||
|
|||
accessRepository.save(access); |
|||
|
|||
setChanged(); |
|||
notifyObservers(new AccountCreatedEvent(access.getName(), |
|||
access.getUserId())); |
|||
|
|||
System.out.println("DBLogin: " + access); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private boolean userNameDoesNotExist(final String name) |
|||
{ |
|||
final Access user = accessRepository.findByUserName(name); |
|||
return user != null; |
|||
} |
|||
|
|||
public boolean logout(final String header) |
|||
{ |
|||
try |
|||
{ |
|||
System.out.println("logout " + header); |
|||
|
|||
final Access access = accessRepository.findByToken(header.substring("Bearer ".length())); |
|||
|
|||
access.removeToken(); |
|||
|
|||
accessRepository.save(access); |
|||
|
|||
return true; |
|||
} catch (Exception e) |
|||
{ |
|||
e.printStackTrace(); |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public LoginRequest authorization(final String header) |
|||
{ |
|||
System.out.println("authorization"); |
|||
final String auth = header.substring("Basic ".length()); |
|||
|
|||
try |
|||
{ |
|||
byte[] message = Base64.getDecoder().decode(auth); |
|||
|
|||
String messageStr = new String(message); |
|||
String[] user_password = messageStr.split(":"); |
|||
|
|||
final Access access = accessRepository.findByUserName(user_password[0]); |
|||
|
|||
System.out.println(access.getName()); |
|||
if (user_password[1].equals(access.getPassword())) |
|||
{ |
|||
access.setToken(AccessToken.of(access)); |
|||
accessRepository.save(access); |
|||
|
|||
final LoginRequest loginRequest = new LoginRequest(); |
|||
loginRequest.setUserId(access.getUserId().getIdentifier()); |
|||
loginRequest.setToken(access.getToken().getToken()); |
|||
loginRequest.setName(access.getName()); |
|||
|
|||
return loginRequest; |
|||
} |
|||
|
|||
return null; |
|||
} catch (Exception e) |
|||
{ |
|||
e.printStackTrace(); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public boolean deleteAccount(final UserId userId, final AccessToken token) |
|||
{ |
|||
if (!token.getUserId().getIdentifier() |
|||
.equals(userId.getIdentifier())) |
|||
{ |
|||
return false; |
|||
} |
|||
if (accessRepository.deleteLoginByUserId(userId).wasAcknowledged()) |
|||
{ |
|||
setChanged(); |
|||
notifyObservers(new AccountDeletedEvent(userId)); |
|||
|
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,62 @@ |
|||
package de.fd.fh.server.access; |
|||
|
|||
import de.fd.fh.server.user.UserId; |
|||
import dev.morphia.annotations.Embedded; |
|||
import dev.morphia.annotations.PrePersist; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Getter; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.util.Random; |
|||
|
|||
@Embedded |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Getter |
|||
public class AccessToken |
|||
{ |
|||
private String token; |
|||
|
|||
private LocalDateTime createdDate; |
|||
|
|||
private Role role; |
|||
|
|||
@Embedded |
|||
private UserId userId; |
|||
|
|||
static AccessToken of(final Access access) |
|||
{ |
|||
return new AccessToken( |
|||
generateToken(), |
|||
LocalDateTime.now(), |
|||
access.getRole(), |
|||
access.getUserId() |
|||
); |
|||
} |
|||
|
|||
@PrePersist |
|||
void prePersist() |
|||
{ |
|||
this.createdDate = LocalDateTime.now(); |
|||
} |
|||
|
|||
private static String generateToken() |
|||
{ |
|||
final String upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
|||
final String lower = upper.toLowerCase(); |
|||
final String numbers = "0123456789"; |
|||
final String alphabet = upper + lower + numbers; |
|||
|
|||
System.out.println("generate Security Token."); |
|||
|
|||
Random random = new Random(); |
|||
StringBuilder generatedString = new StringBuilder(); |
|||
for (int i = 0; i < 64; i++) { |
|||
generatedString.append(alphabet.charAt(random.nextInt(alphabet.length()))); |
|||
} |
|||
|
|||
System.out.println("Token: " + generatedString); |
|||
return generatedString.toString(); |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
package de.fd.fh.server.access.events; |
|||
|
|||
import de.fd.fh.server.user.UserId; |
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
@RequiredArgsConstructor |
|||
@Getter |
|||
public class AccountCreatedEvent |
|||
{ |
|||
private final String name; |
|||
private final UserId userId; |
|||
} |
@ -0,0 +1,12 @@ |
|||
package de.fd.fh.server.access.events; |
|||
|
|||
import de.fd.fh.server.user.UserId; |
|||
import lombok.Getter; |
|||
import lombok.RequiredArgsConstructor; |
|||
|
|||
@RequiredArgsConstructor |
|||
@Getter |
|||
public class AccountDeletedEvent |
|||
{ |
|||
private final UserId userId; |
|||
} |
@ -0,0 +1,245 @@ |
|||
package de.fd.fh.server.access; |
|||
|
|||
import com.mongodb.WriteResult; |
|||
import de.fd.fh.server.user.UserId; |
|||
import de.fd.fh.shared.network.messages.LoginRequest; |
|||
import de.fd.fh.shared.network.messages.RegistrateRequest; |
|||
import org.junit.jupiter.api.BeforeEach; |
|||
import org.junit.jupiter.api.Test; |
|||
import org.mockito.ArgumentCaptor; |
|||
import spark.HaltException; |
|||
|
|||
import java.time.LocalDateTime; |
|||
import java.util.Base64; |
|||
import java.util.Observable; |
|||
import java.util.Observer; |
|||
|
|||
import static org.hamcrest.MatcherAssert.assertThat; |
|||
import static org.hamcrest.Matchers.notNullValue; |
|||
import static org.junit.jupiter.api.Assertions.*; |
|||
import static org.mockito.ArgumentMatchers.any; |
|||
import static org.mockito.BDDMockito.then; |
|||
import static org.mockito.Mockito.*; |
|||
|
|||
class AccessServiceTest implements Observer |
|||
{ |
|||
private Object event; |
|||
|
|||
@BeforeEach |
|||
void before() |
|||
{ |
|||
this.event = null; |
|||
} |
|||
|
|||
@Test |
|||
void given_authenticatedUser_when_serverAuthenticateUser_should_authenticateUser() |
|||
{ |
|||
final Access access = new Access( |
|||
"testId", |
|||
"testName", |
|||
"testPwd", |
|||
UserId.of("12345"), |
|||
new AccessToken( |
|||
"testToken", |
|||
LocalDateTime.now(), |
|||
Role.USER, |
|||
UserId.of("12345") |
|||
), |
|||
Role.USER); |
|||
|
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.findByToken(any())) |
|||
.thenReturn(access); |
|||
final String path = "/test/path"; |
|||
final String token = "testToken"; |
|||
|
|||
final AccessToken result = new AccessService(repository).before(path, token); |
|||
|
|||
assertThat("User is not authenticated", result, notNullValue()); |
|||
then(repository).should().findByToken(any()); |
|||
then(repository).shouldHaveNoMoreInteractions(); |
|||
} |
|||
|
|||
@Test |
|||
void given_notAuthenticatedUser_when_serverAuthenticateUser_should_denyUser() |
|||
{ |
|||
final Access access = new Access( |
|||
"testId", |
|||
"testName", |
|||
"testPwd", |
|||
UserId.of("12345"), |
|||
null, |
|||
Role.USER); |
|||
|
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.findByToken(any())) |
|||
.thenReturn(access); |
|||
final String path = "/test/path"; |
|||
final String token = "testToken"; |
|||
|
|||
assertThrows(HaltException.class, () ->new AccessService(repository).before(path, token)); |
|||
|
|||
then(repository).should().findByToken(any()); |
|||
then(repository).shouldHaveNoMoreInteractions(); |
|||
} |
|||
|
|||
@Test |
|||
void given_newUser_when_createUser_should_storeNewUser() |
|||
{ |
|||
final RegistrateRequest request = |
|||
RegistrateRequest.of("testUser", "testPwd"); |
|||
|
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.findByUserName(any())) |
|||
.thenReturn(null); |
|||
ArgumentCaptor<Access> accessCaptor = ArgumentCaptor.forClass(Access.class); |
|||
|
|||
final AccessService service = new AccessService(repository); |
|||
service.addObserver(this); |
|||
|
|||
final boolean result = service.createPlayer(request); |
|||
|
|||
assertTrue(result); |
|||
assertThat("No event thrown", this.event, notNullValue()); |
|||
|
|||
verify(repository).save(accessCaptor.capture()); |
|||
final Access createdAccess = accessCaptor.getValue(); |
|||
|
|||
assertNotNull(createdAccess, "No Access created"); |
|||
assertNotNull(createdAccess.get_id(), "No Id created"); |
|||
assertNotNull(createdAccess.getUserId(), "No UserId created"); |
|||
assertEquals("testUser", createdAccess.getName(), "Wrong Username"); |
|||
assertEquals("testPwd", createdAccess.getPassword(), "Wrong Password"); |
|||
assertEquals(Role.USER.name(), createdAccess.getRole().name(), "Should be USER"); |
|||
assertNull(createdAccess.getToken(), "User should not be logged in"); |
|||
|
|||
then(repository).should().findByUserName(any()); |
|||
then(repository).should().save(any(Access.class)); |
|||
then(repository).shouldHaveNoMoreInteractions(); |
|||
} |
|||
|
|||
@Test |
|||
void given_loggedInUser_when_logout_should_logoutUser() |
|||
{ |
|||
final Access access = new Access( |
|||
"testId", |
|||
"testName", |
|||
"testPwd", |
|||
UserId.of("12345"), |
|||
null, |
|||
Role.USER |
|||
); |
|||
final AccessToken token = AccessToken.of(access); |
|||
access.setToken(token); |
|||
|
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.findByToken(any())) |
|||
.thenReturn(access); |
|||
ArgumentCaptor<Access> accessCaptor = ArgumentCaptor.forClass(Access.class); |
|||
|
|||
final AccessService service = new AccessService(repository); |
|||
|
|||
final boolean result = service.logout("testToken"); |
|||
|
|||
assertTrue(result); |
|||
|
|||
verify(repository).save(accessCaptor.capture()); |
|||
final Access createdAccess = accessCaptor.getValue(); |
|||
|
|||
assertNotNull(createdAccess, "No Access created"); |
|||
assertNotNull(createdAccess.get_id(), "No Id created"); |
|||
assertNotNull(createdAccess.getUserId(), "No UserId created"); |
|||
assertEquals("testName", createdAccess.getName(), "Wrong Username"); |
|||
assertEquals("testPwd", createdAccess.getPassword(), "Wrong Password"); |
|||
assertEquals(Role.USER.name(), createdAccess.getRole().name(), "Should be USER"); |
|||
assertNull(createdAccess.getToken(), "User should logged out"); |
|||
|
|||
then(repository).should().findByToken(any()); |
|||
then(repository).should().save(any(Access.class)); |
|||
then(repository).shouldHaveNoMoreInteractions(); |
|||
} |
|||
|
|||
@Test |
|||
void given_storedUser_when_loginUser_should_returnLoginRequest() |
|||
{ |
|||
final byte[] message = Base64.getEncoder().encode("testName:testPassword".getBytes()); |
|||
final String header = "Basic " + new String(message); |
|||
final Access access = new Access( |
|||
"testId", |
|||
"testName", |
|||
"testPassword", |
|||
UserId.of("12345"), |
|||
null, |
|||
Role.USER |
|||
); |
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.findByUserName(any())) |
|||
.thenReturn(access); |
|||
|
|||
final LoginRequest result = new AccessService(repository).authorization(header); |
|||
|
|||
assertNotNull(result); |
|||
assertEquals(result.getName(), "testName", "Wrong UserName"); |
|||
assertEquals(result.getUserId(), "12345", "Wrong Password"); |
|||
assertNotNull(result.getToken(), "Not logged in"); |
|||
} |
|||
|
|||
@Test |
|||
void given_storedUserWithWrongPassword_when_loginUser_should_returnAccessDeny() |
|||
{ |
|||
final byte[] message = Base64.getEncoder().encode("testName:testPassword".getBytes()); |
|||
final String header = "Basic " + new String(message); |
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
|
|||
final LoginRequest result = new AccessService(repository).authorization(header); |
|||
|
|||
assertNull(result, "Return LoginRequest but wrong permissions"); |
|||
} |
|||
|
|||
@Test |
|||
void given_storedUser_when_deleteUser_should_deleteUser() |
|||
{ |
|||
final UserId userId = UserId.of("12345"); |
|||
final AccessToken token = new AccessToken(null, null, null, UserId.of("12345")); |
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.deleteLoginByUserId(any(UserId.class))) |
|||
.thenReturn(new WriteResult(1, false, null)); |
|||
|
|||
final AccessService service = new AccessService(repository); |
|||
service.addObserver(this); |
|||
|
|||
final boolean result = service.deleteAccount(userId, token); |
|||
|
|||
assertTrue(result); |
|||
assertNotNull(event); |
|||
|
|||
then(repository).should().deleteLoginByUserId(any(UserId.class)); |
|||
then(repository).shouldHaveNoMoreInteractions(); |
|||
} |
|||
|
|||
@Test |
|||
void given_storedUser_when_deleteUserWithWrongPermission_should_doNothing() |
|||
{ |
|||
final UserId userId = UserId.of("12345"); |
|||
final AccessToken token = new AccessToken(null, null, null, UserId.of("98765")); |
|||
final AccessRepository repository = mock(AccessRepository.class); |
|||
when(repository.deleteLoginByUserId(any(UserId.class))) |
|||
.thenReturn(new WriteResult(1, false, null)); |
|||
|
|||
final AccessService service = new AccessService(repository); |
|||
service.addObserver(this); |
|||
|
|||
final boolean result = service.deleteAccount(userId, token); |
|||
|
|||
assertFalse(result); |
|||
assertNull(event); |
|||
|
|||
then(repository).shouldHaveNoInteractions(); |
|||
} |
|||
|
|||
@Override |
|||
public void update(Observable o, Object arg) |
|||
{ |
|||
this.event = arg; |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue