Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whitelist #628

Merged
merged 11 commits into from
Dec 25, 2023
19 changes: 19 additions & 0 deletions src/main/java/com/softserveinc/dokazovi/config/AuthConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.softserveinc.dokazovi.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConfigurationProperties(prefix = "auth-config")
public class AuthConfig {

private boolean useXForwardedFor;

public boolean isUseXForwardedFor() {
return useXForwardedFor;
}

public void setUseXForwardedFor(boolean useXForwardedFor) {
this.useXForwardedFor = useXForwardedFor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@
import com.softserveinc.dokazovi.entity.enumerations.UserStatus;
import com.softserveinc.dokazovi.exception.BadRequestException;
import com.softserveinc.dokazovi.exception.TokenRefreshException;
import com.softserveinc.dokazovi.security.RefreshTokenService;
import com.softserveinc.dokazovi.security.TokenProvider;
import com.softserveinc.dokazovi.security.UserPrincipal;
import com.softserveinc.dokazovi.service.LogForLoginService;
import com.softserveinc.dokazovi.service.ProviderService;
import com.softserveinc.dokazovi.service.UserIpWhitelistService;
import com.softserveinc.dokazovi.service.UserLoginIpService;
import com.softserveinc.dokazovi.service.UserService;
import com.softserveinc.dokazovi.service.impl.MailSenderServiceImpl;
import com.softserveinc.dokazovi.security.RefreshTokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
Expand All @@ -34,7 +36,6 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import java.sql.Timestamp;
import java.time.LocalDateTime;

Expand All @@ -57,6 +58,8 @@ public class AuthController {
private final ProviderService providerService;
private final RefreshTokenService refreshTokenService;
private final LogForLoginService logForLoginService;
private final UserLoginIpService userLoginIpService;
private final UserIpWhitelistService userIpWhitelistService;

/**
* Authenticates user using email and password.
Expand Down Expand Up @@ -84,6 +87,15 @@ public ResponseEntity<AuthResponse> authenticateUser(@Valid @RequestBody LoginRe
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);

String userIp = userLoginIpService.getClientIp(request);
userLoginIpService.saveUserIP(userEntity.getId(), userIp);

if (userEntity.getWhitelist() && !userIpWhitelistService.isIpWhitelisted(userEntity.getId(), userIp)) {
status = "The IP wasn't found";
throw new BadCredentialsException("You are not allowed to log in from this device");
}

if (!userEntity.getEnabled()) {
throw new BadRequestException("Please confirm your email!");
} else if (userEntity.getStatus() != UserStatus.ACTIVE) {
Expand Down Expand Up @@ -116,7 +128,7 @@ public ResponseEntity<AuthResponse> authenticateUser(@Valid @RequestBody LoginRe
logForLoginService.save(LogForLoginEntity.builder()
.login(loginRequest.getEmail())
.dateOfLogin(Timestamp.valueOf(LocalDateTime.now()))
.ip(request.getRemoteAddr())
.ip(userLoginIpService.getClientIp(request))
.loginStatus(status)
.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public final class EndPoints {
public static final String USER_CHANGE_PASSWORD = "/change-password";
public static final String USER_UPDATE_PASSWORD = "/update-password";
public static final String USER_CHECK_TOKEN = "/check-token";
public static final String USER_WHITELIST_STATUS = "/whitelist-status";
public static final String USER_WHITELIST = "/update-whitelist";
public static final String USER_IP_LIST = "/ip-list";
public static final String USER_RANDOM_EXPERTS = "/random-experts";
public static final String USER_ALL_EXPERTS = "/all-experts";
public static final String USER_ALL_EMAILS = "/all-emails";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@
import com.softserveinc.dokazovi.dto.user.UserEmailPasswordDTO;
import com.softserveinc.dokazovi.dto.user.UserEnabledDTO;
import com.softserveinc.dokazovi.dto.user.UserIdEmailDTO;
import com.softserveinc.dokazovi.dto.user.UserIpWhitelistDTO;
import com.softserveinc.dokazovi.dto.user.UserLoginIpResponseDTO;
import com.softserveinc.dokazovi.dto.user.UserPasswordDTO;
import com.softserveinc.dokazovi.dto.user.UserPublicAndPrivateEmailDTO;
import com.softserveinc.dokazovi.dto.user.UserStatusDTO;
import com.softserveinc.dokazovi.dto.user.UserWhitelistStatusDTO;
import com.softserveinc.dokazovi.entity.PasswordResetTokenEntity;
import com.softserveinc.dokazovi.entity.UserEntity;
import com.softserveinc.dokazovi.pojo.UserSearchCriteria;
import com.softserveinc.dokazovi.security.UserPrincipal;
import com.softserveinc.dokazovi.service.DirectionService;
import com.softserveinc.dokazovi.service.PasswordResetTokenService;
import com.softserveinc.dokazovi.service.UserIpWhitelistService;
import com.softserveinc.dokazovi.service.UserLoginIpService;
import com.softserveinc.dokazovi.service.UserService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
Expand All @@ -30,9 +35,11 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
Expand All @@ -59,9 +66,12 @@
import static com.softserveinc.dokazovi.controller.EndPoints.USER_GET_AUTHORITIES;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_GET_CURRENT_USER;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_GET_USER_BY_ID;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_IP_LIST;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_RANDOM_EXPERTS;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_RESET_PASSWORD;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_UPDATE_PASSWORD;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_WHITELIST;
import static com.softserveinc.dokazovi.controller.EndPoints.USER_WHITELIST_STATUS;

/**
* The User controller is responsible for handling requests for users.
Expand All @@ -75,6 +85,8 @@ public class UserController {
private final UserService userService;
private final DirectionService directionService;
private final PasswordResetTokenService passwordResetTokenService;
private final UserIpWhitelistService userIpWhitelistService;
private final UserLoginIpService userLoginIpService;

/**
* Gets preview of random experts, filtered by directions. Default 12 max per page.
Expand Down Expand Up @@ -299,4 +311,49 @@ public ResponseEntity<UserStatusDTO> changeUserStatus(
userService.changeStatus(userStatusDTO);
return ResponseEntity.status(HttpStatus.OK).build();
}

@PatchMapping(USER_WHITELIST_STATUS)
@PreAuthorize("hasAuthority('EDIT_AUTHOR')")
@ApiOperation(value = "Enables ot disables whitelist option for a specific user")
@ApiResponses(value = {
@ApiResponse(code = 200, message = HttpStatuses.OK),
@ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND)
})
public ResponseEntity<UserWhitelistStatusDTO> changeUserWhitelistStatus(
@AuthenticationPrincipal UserPrincipal userPrincipal,
@RequestBody UserWhitelistStatusDTO userWhitelistStatusDTO) {
userService.changeWhitelistStatus(userPrincipal, userWhitelistStatusDTO);
return ResponseEntity.status(HttpStatus.OK).build();
}

@PutMapping(USER_WHITELIST)
@PreAuthorize("hasAuthority('EDIT_AUTHOR')")
@ApiOperation(value = "Updates whitelist for a specific user")
@ApiResponses(value = {
@ApiResponse(code = 200, message = HttpStatuses.OK),
@ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND)
})
public ResponseEntity<UserIpWhitelistDTO> updateUserWhitelist(
@AuthenticationPrincipal UserPrincipal userPrincipal,
@RequestBody UserIpWhitelistDTO userIpWhitelistDTO
) {
userIpWhitelistService.updateUserIpWhitelist(userPrincipal, userIpWhitelistDTO);
return ResponseEntity.status(HttpStatus.OK).build();
}

@GetMapping(USER_GET_USER_BY_ID + USER_IP_LIST)
@PreAuthorize("hasAuthority('EDIT_AUTHOR')")
@ApiOperation(value = "Gets all IPs that user used to log in")
@ApiResponses(value = {
@ApiResponse(code = 200, message = HttpStatuses.OK),
@ApiResponse(code = 404, message = HttpStatuses.NOT_FOUND)
})
public ResponseEntity<UserLoginIpResponseDTO> getUserLoginIps(
@AuthenticationPrincipal UserPrincipal userPrincipal,
@PathVariable Integer userId
) {
List<String> ips = userLoginIpService.getAllUserIps(userPrincipal, userId);
UserLoginIpResponseDTO responseDTO = new UserLoginIpResponseDTO(ips);
return ResponseEntity.ok(responseDTO);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.softserveinc.dokazovi.dto.user;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserIpWhitelistDTO {
private Integer id;
private List<String> whitelistIps;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.softserveinc.dokazovi.dto.user;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginIpResponseDTO {
private List<String> ipAddresses;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.softserveinc.dokazovi.dto.user;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.NotBlank;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserWhitelistStatusDTO {

private Integer id;

@NotBlank
private boolean whitelistStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,8 @@ public PostEntity getLatestExpertPost() {
public boolean getEnabled() {
return this.enabled;
}

@Builder.Default
@Column(name = "whitelist", nullable = false)
private Boolean whitelist = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.softserveinc.dokazovi.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "user_ip_whitelist")
public class UserIpWhitelistEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private UserEntity user;

@Column(name = "whitelist_ip", nullable = false)
private String whitelistIp;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.softserveinc.dokazovi.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "user_login_ips")
public class UserLoginIpEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private UserEntity user;

@Column(name = "ip_address", nullable = false)
private String ipAddress;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.softserveinc.dokazovi.repositories;

import com.softserveinc.dokazovi.entity.UserIpWhitelistEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.softserveinc.dokazovi.entity.UserEntity;

import java.util.List;

@Repository
public interface UserIpWhitelistRepository extends JpaRepository<UserIpWhitelistEntity, Integer> {
List<UserIpWhitelistEntity> findAllByUser(UserEntity user);

boolean existsByUserAndWhitelistIp(UserEntity user, String whitelistIp);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.softserveinc.dokazovi.repositories;

import com.softserveinc.dokazovi.entity.UserLoginIpEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserLoginIpRepository extends JpaRepository<UserLoginIpEntity, Integer> {
boolean existsByUserIdAndIpAddress(Integer userId, String ipAddress);

List<UserLoginIpEntity> findAllByUserId(Integer userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.softserveinc.dokazovi.service;

import com.softserveinc.dokazovi.security.UserPrincipal;
import org.springframework.stereotype.Service;

@Service
public interface CheckAuthorityService {
boolean checkAuthority(UserPrincipal userPrincipal, String authority);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.softserveinc.dokazovi.service;

import com.softserveinc.dokazovi.dto.user.UserIpWhitelistDTO;
import com.softserveinc.dokazovi.security.UserPrincipal;
import org.springframework.stereotype.Service;

@Service
public interface UserIpWhitelistService {

void updateUserIpWhitelist(UserPrincipal userPrincipal, UserIpWhitelistDTO userIpWhitelistDTO);

boolean isIpWhitelisted(Integer userId, String ip);
}
Loading
Loading