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