/user/devices route
All checks were successful
Deploy API / deploy (ubuntu-latest, 2.44.0) (push) Successful in 47s
All checks were successful
Deploy API / deploy (ubuntu-latest, 2.44.0) (push) Successful in 47s
This commit is contained in:
parent
562fd568e2
commit
c5841402f3
6
pom.xml
6
pom.xml
@ -104,6 +104,12 @@
|
||||
<version>1.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
<version>7.28.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Unirest -->
|
||||
<dependency>
|
||||
|
@ -3,6 +3,7 @@ package cc.pulseapp.api.controller.v1;
|
||||
import cc.pulseapp.api.exception.impl.BadRequestException;
|
||||
import cc.pulseapp.api.model.user.User;
|
||||
import cc.pulseapp.api.model.user.UserDTO;
|
||||
import cc.pulseapp.api.model.user.device.Device;
|
||||
import cc.pulseapp.api.model.user.input.CompleteOnboardingInput;
|
||||
import cc.pulseapp.api.model.user.input.DisableTFAInput;
|
||||
import cc.pulseapp.api.model.user.input.EnableTFAInput;
|
||||
@ -99,7 +100,7 @@ public final class UserController {
|
||||
}
|
||||
|
||||
/**
|
||||
* A POST endpoint to disable TFA for a useer.
|
||||
* A POST endpoint to disable TFA for a user.
|
||||
*
|
||||
* @param input the input to process
|
||||
* @return the disabled response
|
||||
@ -111,6 +112,17 @@ public final class UserController {
|
||||
return ResponseEntity.ok(Map.of("success", true));
|
||||
}
|
||||
|
||||
/**
|
||||
* A GET endpoint to get the
|
||||
* devices logged into the user.
|
||||
*
|
||||
* @return the devices
|
||||
*/
|
||||
@GetMapping("/devices") @ResponseBody @NonNull
|
||||
public ResponseEntity<List<Device>> getDevices() {
|
||||
return ResponseEntity.ok(userService.getDevices());
|
||||
}
|
||||
|
||||
/**
|
||||
* A POST endpoint to logout the user.
|
||||
*
|
||||
|
@ -0,0 +1,11 @@
|
||||
package cc.pulseapp.api.model.user.device;
|
||||
|
||||
/**
|
||||
* The type of browser used
|
||||
* by a {@link Device}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public enum BrowserType {
|
||||
FIREFOX, EDGE, CHROME, SAFARI, SAMSUNGBROWSER, UNKNOWN
|
||||
}
|
74
src/main/java/cc/pulseapp/api/model/user/device/Device.java
Normal file
74
src/main/java/cc/pulseapp/api/model/user/device/Device.java
Normal file
@ -0,0 +1,74 @@
|
||||
package cc.pulseapp.api.model.user.device;
|
||||
|
||||
import cc.pulseapp.api.model.user.User;
|
||||
import cc.pulseapp.api.model.user.session.Session;
|
||||
import cc.pulseapp.api.model.user.session.SessionLocation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* A device logged into a
|
||||
* {@link User}'s account.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
@AllArgsConstructor @Getter @ToString
|
||||
public final class Device {
|
||||
/**
|
||||
* The type of this device.
|
||||
*/
|
||||
@NonNull private final DeviceType type;
|
||||
|
||||
/**
|
||||
* The browser type of this device.
|
||||
*/
|
||||
@NonNull private final BrowserType browserType;
|
||||
|
||||
/**
|
||||
* The IP address of this device.
|
||||
*/
|
||||
@NonNull private final String ip;
|
||||
|
||||
/**
|
||||
* The location of this device, if known.
|
||||
*/
|
||||
private final String location;
|
||||
|
||||
/**
|
||||
* The user agent of this device.
|
||||
*/
|
||||
@NonNull private final String userAgent;
|
||||
|
||||
/**
|
||||
* The session snowflake associated with this device.
|
||||
*/
|
||||
private final long sessionSnowflake;
|
||||
|
||||
/**
|
||||
* The date this device first logged into the account.
|
||||
*/
|
||||
private final Date firstLogin;
|
||||
|
||||
/**
|
||||
* Construct a device from a session.
|
||||
*
|
||||
* @param session the session
|
||||
* @param deviceType the device type
|
||||
* @param browserType the device browser type
|
||||
* @param firstLogin the sessions first login time
|
||||
* @return the constructed device
|
||||
*/
|
||||
@NonNull
|
||||
public static Device fromSession(@NonNull Session session, @NonNull DeviceType deviceType,
|
||||
@NonNull BrowserType browserType, @NonNull Date firstLogin) {
|
||||
SessionLocation rawLocation = session.getLocation();
|
||||
String location = rawLocation.getCountry() == null ? null
|
||||
: rawLocation.getCity() + ", " + rawLocation.getRegion() + ", " + rawLocation.getCountry();
|
||||
return new Device(deviceType, browserType, rawLocation.getIp(), location,
|
||||
rawLocation.getUserAgent(), session.getSnowflake(), firstLogin);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package cc.pulseapp.api.model.user.device;
|
||||
|
||||
/**
|
||||
* The type of a {@link Device}.
|
||||
*
|
||||
* @author Braydon
|
||||
*/
|
||||
public enum DeviceType {
|
||||
DESKTOP, TABLET, PHONE, UNKNOWN
|
||||
}
|
@ -21,7 +21,7 @@ public final class Session {
|
||||
/**
|
||||
* The snowflake of this session.
|
||||
*/
|
||||
@EqualsAndHashCode.Include @Id @JsonIgnore private final long snowflake;
|
||||
@EqualsAndHashCode.Include @Id private final long snowflake;
|
||||
|
||||
/**
|
||||
* The snowflake of the user this session is for.
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cc.pulseapp.api.service;
|
||||
|
||||
import cc.pulseapp.api.common.EnvironmentUtils;
|
||||
import cc.pulseapp.api.exception.impl.BadRequestException;
|
||||
import cc.pulseapp.api.model.IGenericResponse;
|
||||
import com.google.gson.JsonObject;
|
||||
@ -26,8 +27,6 @@ public final class CaptchaService {
|
||||
* @throws BadRequestException if the response is invalid
|
||||
*/
|
||||
public void validateCaptcha(@NonNull String captchaResponse) throws BadRequestException {
|
||||
System.out.println("captchaResponse = " + captchaResponse);
|
||||
|
||||
JsonObject body = new JsonObject();
|
||||
body.addProperty("secret", secretKey);
|
||||
body.addProperty("response", captchaResponse);
|
||||
@ -35,8 +34,7 @@ public final class CaptchaService {
|
||||
.header(HttpHeaders.CONTENT_TYPE, "application/json")
|
||||
.body(body)
|
||||
.asJson();
|
||||
System.out.println("response = " + response.getBody().toPrettyString());
|
||||
if (/*EnvironmentUtils.isProduction() && */!response.getBody().getObject().getBoolean("success")) {
|
||||
if (EnvironmentUtils.isProduction() && !response.getBody().getObject().getBoolean("success")) {
|
||||
throw new BadRequestException(Error.CAPTCHA_INVALID);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ import cc.pulseapp.api.model.user.TFAProfile;
|
||||
import cc.pulseapp.api.model.user.User;
|
||||
import cc.pulseapp.api.model.user.UserDTO;
|
||||
import cc.pulseapp.api.model.user.UserFlag;
|
||||
import cc.pulseapp.api.model.user.device.BrowserType;
|
||||
import cc.pulseapp.api.model.user.device.Device;
|
||||
import cc.pulseapp.api.model.user.device.DeviceType;
|
||||
import cc.pulseapp.api.model.user.input.CompleteOnboardingInput;
|
||||
import cc.pulseapp.api.model.user.input.DisableTFAInput;
|
||||
import cc.pulseapp.api.model.user.input.EnableTFAInput;
|
||||
@ -21,6 +24,9 @@ import cc.pulseapp.api.repository.UserRepository;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import lombok.NonNull;
|
||||
import nl.basjes.parse.useragent.UserAgent;
|
||||
import nl.basjes.parse.useragent.UserAgentAnalyzer;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -35,6 +41,12 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
@Service
|
||||
public final class UserService {
|
||||
private static final UserAgentAnalyzer userAgentAnalyzer = UserAgentAnalyzer
|
||||
.newBuilder()
|
||||
.useJava8CompatibleCaching()
|
||||
.withCache(10000)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* The auth service to use.
|
||||
*/
|
||||
@ -60,11 +72,6 @@ public final class UserService {
|
||||
*/
|
||||
@NonNull private final TFAService tfaService;
|
||||
|
||||
/**
|
||||
* The captcha service to use.
|
||||
*/
|
||||
@NonNull private final CaptchaService captchaService;
|
||||
|
||||
/**
|
||||
* The user repository to use.
|
||||
*/
|
||||
@ -89,14 +96,13 @@ public final class UserService {
|
||||
@Autowired
|
||||
public UserService(@NonNull AuthService authService, @NonNull SnowflakeService snowflakeService,
|
||||
@NonNull OrganizationService orgService, @NonNull StatusPageService statusPageService,
|
||||
@NonNull TFAService tfaService, @NonNull CaptchaService captchaService,
|
||||
@NonNull UserRepository userRepository, @NonNull SessionRepository sessionRepository) {
|
||||
@NonNull TFAService tfaService, @NonNull UserRepository userRepository,
|
||||
@NonNull SessionRepository sessionRepository) {
|
||||
this.authService = authService;
|
||||
this.snowflakeService = snowflakeService;
|
||||
this.orgService = orgService;
|
||||
this.statusPageService = statusPageService;
|
||||
this.tfaService = tfaService;
|
||||
this.captchaService = captchaService;
|
||||
this.userRepository = userRepository;
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
@ -245,6 +251,31 @@ public final class UserService {
|
||||
userRepository.save(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the devices logged into
|
||||
* the authenticated user.
|
||||
*
|
||||
* @return the devices
|
||||
*/
|
||||
@NonNull
|
||||
public List<Device> getDevices() {
|
||||
List<Device> devices = new ArrayList<>();
|
||||
User user = authService.getAuthenticatedUser();
|
||||
for (Session session : sessionRepository.findAllByUserSnowflake(user.getSnowflake())) {
|
||||
UserAgent.ImmutableUserAgent userAgent = userAgentAnalyzer.parse(session.getLocation().getUserAgent());
|
||||
DeviceType deviceType = EnumUtils.getEnum(DeviceType.class, userAgent.get("DeviceClass").getValue().toUpperCase());
|
||||
BrowserType browserType = EnumUtils.getEnum(BrowserType.class, userAgent.get("AgentName").getValue().toUpperCase());
|
||||
if (deviceType == null) {
|
||||
deviceType = DeviceType.UNKNOWN;
|
||||
}
|
||||
if (browserType == null) {
|
||||
browserType = BrowserType.UNKNOWN;
|
||||
}
|
||||
devices.add(Device.fromSession(session, deviceType, browserType, new Date(snowflakeService.extractCreationTime(session.getSnowflake()))));
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the user.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user