Skip to content

Commit 5fa26cb

Browse files
committed
11_14_vds_restrict_update
1 parent 5b8a16d commit 5fa26cb

10 files changed

Lines changed: 131 additions & 15 deletions

File tree

config/messages/app.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ common.cancel=Cancel
5050
common.search=Search
5151
5252
exception.user.duplicateEmail=User with this email already exists
53+
exception.user.updateRestriction=Admin/User update is forbidden
5354
exception.meal.duplicateDateTime=You already have meal with this date/time
5455
5556
error.appError=Application error

config/messages/app_ru.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ common.cancel=Отменить
5050
common.search=Искать
5151

5252
exception.user.duplicateEmail=Пользователь с такой почтой уже есть в приложении
53+
exception.user.updateRestriction=Изменение Admin/User запрещено
5354
exception.meal.duplicateDateTime=У вас уже есть еда с такой датой/временем
5455

5556
error.appError=Ошибка приложения

src/main/java/ru/javawebinar/topjava/Profiles.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ public class Profiles {
1212

1313
public static final String
1414
POSTGRES_DB = "postgres",
15-
HSQL_DB = "hsqldb";
15+
HSQL_DB = "hsqldb",
16+
VDS = "vds";
1617

17-
// Get DB profile depending of DB driver in classpath
18+
// Get DB profile depending on DB driver in classpath
1819
public static String getActiveDbProfile() {
1920
if (ClassUtils.isPresent("org.postgresql.Driver", null)) {
2021
return POSTGRES_DB;

src/main/java/ru/javawebinar/topjava/service/UserService.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
package ru.javawebinar.topjava.service;
22

3+
import org.springframework.beans.factory.annotation.Autowired;
34
import org.springframework.cache.annotation.CacheEvict;
45
import org.springframework.cache.annotation.Cacheable;
56
import org.springframework.context.annotation.Scope;
67
import org.springframework.context.annotation.ScopedProxyMode;
8+
import org.springframework.core.env.Environment;
79
import org.springframework.security.core.userdetails.UserDetailsService;
810
import org.springframework.security.core.userdetails.UsernameNotFoundException;
911
import org.springframework.security.crypto.password.PasswordEncoder;
1012
import org.springframework.stereotype.Service;
1113
import org.springframework.transaction.annotation.Transactional;
1214
import org.springframework.util.Assert;
1315
import ru.javawebinar.topjava.AuthorizedUser;
16+
import ru.javawebinar.topjava.Profiles;
17+
import ru.javawebinar.topjava.model.AbstractBaseEntity;
1418
import ru.javawebinar.topjava.model.User;
1519
import ru.javawebinar.topjava.repository.UserRepository;
1620
import ru.javawebinar.topjava.to.UserTo;
1721
import ru.javawebinar.topjava.util.UsersUtil;
22+
import ru.javawebinar.topjava.util.exception.UpdateRestrictionException;
1823

1924
import java.util.List;
2025

@@ -28,6 +33,14 @@ public class UserService implements UserDetailsService {
2833
private final UserRepository repository;
2934
private final PasswordEncoder passwordEncoder;
3035

36+
private boolean modificationRestriction;
37+
38+
@Autowired
39+
@SuppressWarnings("deprecation")
40+
public void setEnvironment(Environment environment) {
41+
modificationRestriction = environment.acceptsProfiles(Profiles.VDS);
42+
}
43+
3144
public UserService(UserRepository repository, PasswordEncoder passwordEncoder) {
3245
this.repository = repository;
3346
this.passwordEncoder = passwordEncoder;
@@ -41,6 +54,7 @@ public User create(User user) {
4154

4255
@CacheEvict(value = "users", allEntries = true)
4356
public void delete(int id) {
57+
checkModificationAllowed(id);
4458
checkNotFound(repository.delete(id), id);
4559
}
4660

@@ -62,20 +76,23 @@ public List<User> getAll() {
6276
public void update(User user) {
6377
Assert.notNull(user, "user must not be null");
6478
// checkNotFound : check works only for JDBC, disabled
79+
checkModificationAllowed(user.id());
6580
prepareAndSave(user);
6681
}
6782

6883

6984
@CacheEvict(value = "users", allEntries = true)
7085
@Transactional
7186
public void update(UserTo userTo) {
87+
checkModificationAllowed(userTo.id());
7288
User user = get(userTo.id());
7389
prepareAndSave(UsersUtil.updateFromTo(user, userTo));
7490
}
7591

7692
@CacheEvict(value = "users", allEntries = true)
7793
@Transactional
7894
public void enable(int id, boolean enabled) {
95+
checkModificationAllowed(id);
7996
User user = get(id);
8097
user.setEnabled(enabled);
8198
repository.save(user); // !! need only for JDBC implementation
@@ -97,4 +114,10 @@ private User prepareAndSave(User user) {
97114
public User getWithMeals(int id) {
98115
return checkNotFound(repository.getWithMeals(id), id);
99116
}
117+
118+
protected void checkModificationAllowed(int id) {
119+
if (modificationRestriction && id < AbstractBaseEntity.START_SEQ + 2) {
120+
throw new UpdateRestrictionException();
121+
}
122+
}
100123
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package ru.javawebinar.topjava.util.exception;
2+
3+
public class ApplicationException extends RuntimeException {
4+
5+
private final ErrorType type;
6+
private final String msgCode;
7+
8+
public ApplicationException(String msgCode, ErrorType type) {
9+
this.msgCode = msgCode;
10+
this.type = type;
11+
}
12+
13+
public String getMsgCode() {
14+
return msgCode;
15+
}
16+
17+
public ErrorType getType() {
18+
return type;
19+
}
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package ru.javawebinar.topjava.util.exception;
2+
3+
public class UpdateRestrictionException extends ApplicationException {
4+
public static final String EXCEPTION_UPDATE_RESTRICTION = "exception.user.updateRestriction";
5+
6+
public UpdateRestrictionException() {
7+
super(EXCEPTION_UPDATE_RESTRICTION, ErrorType.VALIDATION_ERROR);
8+
}
9+
}

src/main/java/ru/javawebinar/topjava/web/ExceptionInfoHandler.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
import org.springframework.web.bind.annotation.RestControllerAdvice;
1515
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
1616
import ru.javawebinar.topjava.util.validation.ValidationUtil;
17-
import ru.javawebinar.topjava.util.exception.ErrorInfo;
18-
import ru.javawebinar.topjava.util.exception.ErrorType;
19-
import ru.javawebinar.topjava.util.exception.IllegalRequestDataException;
20-
import ru.javawebinar.topjava.util.exception.NotFoundException;
17+
import ru.javawebinar.topjava.util.exception.*;
2118

2219
import javax.servlet.http.HttpServletRequest;
2320
import java.util.Map;
@@ -47,6 +44,11 @@ public ResponseEntity<ErrorInfo> notFoundError(HttpServletRequest req, NotFoundE
4744
return logAndGetErrorInfo(req, e, false, DATA_NOT_FOUND);
4845
}
4946

47+
@ExceptionHandler(ApplicationException.class)
48+
public ResponseEntity<ErrorInfo> updateRestrictionError(HttpServletRequest req, ApplicationException appEx) {
49+
return logAndGetErrorInfo(req, appEx, false, appEx.getType(), messageSourceAccessor.getMessage(appEx.getMsgCode()));
50+
}
51+
5052
@ExceptionHandler(DataIntegrityViolationException.class)
5153
public ResponseEntity<ErrorInfo> conflict(HttpServletRequest req, DataIntegrityViolationException e) {
5254
String rootMsg = ValidationUtil.getRootCause(e).getMessage();

src/main/java/ru/javawebinar/topjava/web/GlobalExceptionHandler.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
import org.springframework.web.bind.annotation.ExceptionHandler;
88
import org.springframework.web.servlet.ModelAndView;
99
import org.springframework.web.servlet.NoHandlerFoundException;
10-
import ru.javawebinar.topjava.util.validation.ValidationUtil;
10+
import ru.javawebinar.topjava.util.exception.ApplicationException;
1111
import ru.javawebinar.topjava.util.exception.ErrorType;
12+
import ru.javawebinar.topjava.util.validation.ValidationUtil;
1213

1314
import javax.servlet.http.HttpServletRequest;
1415
import java.util.Map;
@@ -24,21 +25,26 @@ public GlobalExceptionHandler(MessageSourceAccessor messageSourceAccessor) {
2425
}
2526

2627
@ExceptionHandler(NoHandlerFoundException.class)
27-
public ModelAndView wrongRequest(HttpServletRequest req, NoHandlerFoundException e) throws Exception {
28-
return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST);
28+
public ModelAndView wrongRequest(HttpServletRequest req, NoHandlerFoundException e) {
29+
return logAndGetExceptionView(req, e, false, ErrorType.WRONG_REQUEST, null);
30+
}
31+
32+
@ExceptionHandler(ApplicationException.class)
33+
public ModelAndView updateRestrictionException(HttpServletRequest req, ApplicationException appEx) {
34+
return logAndGetExceptionView(req, appEx, false, appEx.getType(), appEx.getMsgCode());
2935
}
3036

3137
@ExceptionHandler(Exception.class)
3238
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
3339
log.error("Exception at request " + req.getRequestURL(), e);
34-
return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR);
40+
return logAndGetExceptionView(req, e, true, ErrorType.APP_ERROR, null);
3541
}
3642

37-
private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType) {
43+
private ModelAndView logAndGetExceptionView(HttpServletRequest req, Exception e, boolean logException, ErrorType errorType, String code) {
3844
Throwable rootCause = ValidationUtil.logAndGetRootCause(log, req, e, logException, errorType);
3945

4046
ModelAndView mav = new ModelAndView("exception",
41-
Map.of("exception", rootCause, "message", ValidationUtil.getMessage(rootCause),
47+
Map.of("exception", rootCause, "message", code != null ? messageSourceAccessor.getMessage(code) : ValidationUtil.getMessage(rootCause),
4248
"typeMessage", messageSourceAccessor.getMessage(errorType.getErrorCode()),
4349
"status", errorType.getStatus()));
4450
mav.setStatus(errorType.getStatus());

src/main/resources/spring/spring-db.xml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
p:password="${database.password}"/>
3535
</beans>
3636

37-
<beans profile="postgres">
37+
<beans profile="postgres,vds">
3838
<!--
3939
For postgres driver logging
4040
It uses java.util.logging and logged via jul-to-slf4j bridge
@@ -51,7 +51,17 @@
5151
p:driverClassName="org.postgresql.Driver"
5252
p:url="${database.url}"
5353
p:username="${database.username}"
54-
p:password="${database.password}"/>
54+
p:password="${database.password}"
55+
p:validationQuery="SELECT 1"
56+
p:maxActive="10"
57+
p:minIdle="2"
58+
p:maxWait="20000"
59+
p:initialSize="2"
60+
p:maxIdle="5"
61+
p:testOnBorrow="true"
62+
p:removeAbandoned="true"
63+
p:testOnConnect="true"
64+
p:testWhileIdle="true"/>
5565
</beans>
5666

5767
<beans profile="tomcat">
@@ -71,7 +81,7 @@
7181

7282
<bean id="transactionManager"
7383
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
74-
<property name="dataSource" ref="dataSource" />
84+
<property name="dataSource" ref="dataSource"/>
7585
</bean>
7686
</beans>
7787

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package ru.javawebinar.topjava.web.user;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.http.MediaType;
5+
import org.springframework.test.context.ActiveProfiles;
6+
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
7+
import ru.javawebinar.topjava.UserTestData;
8+
import ru.javawebinar.topjava.util.exception.ErrorType;
9+
import ru.javawebinar.topjava.web.AbstractControllerTest;
10+
11+
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
12+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
13+
import static ru.javawebinar.topjava.Profiles.VDS;
14+
import static ru.javawebinar.topjava.TestUtil.userHttpBasic;
15+
import static ru.javawebinar.topjava.UserTestData.*;
16+
import static ru.javawebinar.topjava.util.exception.UpdateRestrictionException.EXCEPTION_UPDATE_RESTRICTION;
17+
18+
@ActiveProfiles(VDS)
19+
class VdsRestControllerTest extends AbstractControllerTest {
20+
21+
private static final String REST_URL = AdminRestController.REST_URL + '/';
22+
23+
@Test
24+
void delete() throws Exception {
25+
perform(MockMvcRequestBuilders.delete(REST_URL + USER_ID)
26+
.with(userHttpBasic(admin)))
27+
.andDo(print())
28+
.andExpect(errorType(ErrorType.VALIDATION_ERROR))
29+
.andExpect(detailMessage(EXCEPTION_UPDATE_RESTRICTION))
30+
.andExpect(status().isUnprocessableEntity());
31+
}
32+
33+
@Test
34+
void update() throws Exception {
35+
perform(MockMvcRequestBuilders.put(REST_URL + USER_ID)
36+
.contentType(MediaType.APPLICATION_JSON)
37+
.with(userHttpBasic(admin))
38+
.content(UserTestData.jsonWithPassword(user, "password")))
39+
.andExpect(errorType(ErrorType.VALIDATION_ERROR))
40+
.andExpect(detailMessage(EXCEPTION_UPDATE_RESTRICTION))
41+
.andExpect(status().isUnprocessableEntity());
42+
}
43+
}

0 commit comments

Comments
 (0)