Skip to content

Commit 214b2f9

Browse files
mensindaaurambaj
andauthored
Add an option to restrict which locales a user can edit (#1004)
This PR adds the option to restrict what locales a user can edit. This way, users that are not that well versed with mojito can't accidentally change the translations for wrong locales. --------- Co-authored-by: Jean Aurambault <aurambaj@users.noreply.github.com>
1 parent 40da47a commit 214b2f9

File tree

34 files changed

+655
-32
lines changed

34 files changed

+655
-32
lines changed

cli/src/test/java/com/box/l10n/mojito/cli/command/UserDeleteCommandTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public void testDelete() {
3434
String commonName = "Test Mojito";
3535

3636
userService.createUserWithRole(
37-
username, password, Role.ROLE_USER, givenName, surname, commonName, false);
37+
username, password, Role.ROLE_USER, givenName, surname, commonName, null, true, false);
3838
User user = userRepository.findByUsername(username);
3939
assertNotNull(user);
4040

cli/src/test/java/com/box/l10n/mojito/cli/command/UserUpdateCommandTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ private User createTestUserUsingUserService(String username, String rolename) {
131131
}
132132
User user =
133133
userService.createUserWithRole(
134-
username, password, role, givenName, surname, commonName, false);
134+
username, password, role, givenName, surname, commonName, null, true, false);
135135
return user;
136136
}
137137
}

restclient/src/main/java/com/box/l10n/mojito/rest/entity/User.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ public class User {
2525

2626
private String commonName;
2727

28+
private boolean canTranslateAllLocales;
29+
2830
@JsonManagedReference Set<Authority> authorities = new HashSet<>();
2931

32+
@JsonManagedReference Set<UserLocale> userLocales = new HashSet<>();
33+
3034
public Long getId() {
3135
return id;
3236
}
@@ -90,4 +94,20 @@ public Set<Authority> getAuthorities() {
9094
public void setAuthorities(Set<Authority> authorities) {
9195
this.authorities = authorities;
9296
}
97+
98+
public boolean getCanTranslateAllLocales() {
99+
return canTranslateAllLocales;
100+
}
101+
102+
public void setCanTranslateAllLocales(boolean canTranslateAllLocales) {
103+
this.canTranslateAllLocales = canTranslateAllLocales;
104+
}
105+
106+
public Set<UserLocale> getUserLocales() {
107+
return userLocales;
108+
}
109+
110+
public void setUserLocales(Set<UserLocale> userLocales) {
111+
this.userLocales = userLocales;
112+
}
93113
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.box.l10n.mojito.rest.entity;
2+
3+
import com.fasterxml.jackson.annotation.JsonBackReference;
4+
5+
public class UserLocale {
6+
@JsonBackReference private User user;
7+
8+
private Locale locale;
9+
10+
public User getUser() {
11+
return user;
12+
}
13+
14+
public void setUser(User user) {
15+
this.user = user;
16+
}
17+
18+
public Locale getLocale() {
19+
return locale;
20+
}
21+
22+
public void setLocale(Locale locale) {
23+
this.locale = locale;
24+
}
25+
}

webapp/src/main/java/com/box/l10n/mojito/FlyWayConfig.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ void tryToMigrateIfMysql8Migration(Flyway flyway, FlywayException fe) {
154154
* since a failure to perform the query will show that the DB is not protected.
155155
*
156156
* <p>This is an extra check added to the settings: spring.flyway.clean-disabled=true (now default
157-
* in Mojito) and l10n.flyway.clean=false (that is usually set manually, but can be wrongly enabled)
158-
* and shouldn't be solely relied upon.
157+
* in Mojito) and l10n.flyway.clean=false (that is usually set manually, but can be wrongly
158+
* enabled) and shouldn't be solely relied upon.
159159
*
160160
* <p>For now this is enabled manually in the database with: CREATE TABLE
161161
* flyway_clean_protection(enabled boolean default true); INSERT INTO flyway_clean_protection

webapp/src/main/java/com/box/l10n/mojito/entity/security/user/User.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,16 @@
3636
name = "User.legacy",
3737
attributeNodes = {
3838
@NamedAttributeNode("createdByUser"),
39+
@NamedAttributeNode(value = "userLocales", subgraph = "User.legacy.userLocales"),
3940
@NamedAttributeNode(value = "authorities", subgraph = "User.legacy.authorities")
4041
},
4142
subgraphs = {
4243
@NamedSubgraph(
4344
name = "User.legacy.authorities",
44-
attributeNodes = {@NamedAttributeNode("createdByUser"), @NamedAttributeNode("user")})
45+
attributeNodes = {@NamedAttributeNode("createdByUser"), @NamedAttributeNode("user")}),
46+
@NamedSubgraph(
47+
name = "User.legacy.userLocales",
48+
attributeNodes = {@NamedAttributeNode("user")}),
4549
})
4650
public class User extends AuditableEntity implements Serializable {
4751

@@ -69,6 +73,9 @@ public class User extends AuditableEntity implements Serializable {
6973
@JsonView(View.IdAndName.class)
7074
String commonName;
7175

76+
@Column(name = "can_translate_all_locales", nullable = false)
77+
boolean canTranslateAllLocales = true;
78+
7279
/**
7380
* Sets this flag if the user is created by a process that don't have all the information. Eg.
7481
* pushing an asset for a branch with an owner or header base authentication. If the owner is not
@@ -80,6 +87,10 @@ public class User extends AuditableEntity implements Serializable {
8087
@Column(name = "partially_created")
8188
Boolean partiallyCreated = false;
8289

90+
@JsonManagedReference
91+
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
92+
Set<UserLocale> userLocales = new HashSet<>();
93+
8394
@JsonManagedReference
8495
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
8596
Set<Authority> authorities = new HashSet<>();
@@ -156,6 +167,22 @@ public void setCommonName(String commonName) {
156167
this.commonName = commonName;
157168
}
158169

170+
public boolean getCanTranslateAllLocales() {
171+
return canTranslateAllLocales;
172+
}
173+
174+
public void setCanTranslateAllLocales(boolean canTranslateAllLocales) {
175+
this.canTranslateAllLocales = canTranslateAllLocales;
176+
}
177+
178+
public Set<UserLocale> getUserLocales() {
179+
return userLocales;
180+
}
181+
182+
public void setUserLocales(Set<UserLocale> userLocales) {
183+
this.userLocales = userLocales;
184+
}
185+
159186
public Boolean getPartiallyCreated() {
160187
return partiallyCreated;
161188
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.box.l10n.mojito.entity.security.user;
2+
3+
import com.box.l10n.mojito.entity.BaseEntity;
4+
import com.box.l10n.mojito.entity.Locale;
5+
import com.box.l10n.mojito.rest.View;
6+
import com.fasterxml.jackson.annotation.JsonBackReference;
7+
import com.fasterxml.jackson.annotation.JsonView;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.ForeignKey;
10+
import jakarta.persistence.Index;
11+
import jakarta.persistence.JoinColumn;
12+
import jakarta.persistence.ManyToOne;
13+
import jakarta.persistence.Table;
14+
import java.io.Serializable;
15+
import org.hibernate.annotations.BatchSize;
16+
17+
@Entity
18+
@Table(
19+
name = "user_locale",
20+
indexes = {
21+
@Index(
22+
name = "UK__USER_LOCALE__USER_ID__LOCALE_ID",
23+
columnList = "user_id, locale_id",
24+
unique = true)
25+
})
26+
@BatchSize(size = 1000)
27+
public class UserLocale extends BaseEntity implements Serializable {
28+
29+
@ManyToOne
30+
@JsonBackReference
31+
@JoinColumn(
32+
name = "user_id",
33+
foreignKey = @ForeignKey(name = "FK__USER_LOCALE__USER__ID"),
34+
nullable = false)
35+
User user;
36+
37+
@JsonView(View.LocaleSummary.class)
38+
@ManyToOne
39+
@JoinColumn(
40+
name = "locale_id",
41+
foreignKey = @ForeignKey(name = "FK__USER_LOCALE__LOCALE__ID"),
42+
nullable = false)
43+
Locale locale;
44+
45+
public UserLocale() {}
46+
47+
public UserLocale(User user, Locale locale) {
48+
this.user = user;
49+
this.locale = locale;
50+
}
51+
52+
public User getUser() {
53+
return user;
54+
}
55+
56+
public void setUser(User user) {
57+
this.user = user;
58+
}
59+
60+
public Locale getLocale() {
61+
return locale;
62+
}
63+
64+
public void setLocale(Locale locale) {
65+
this.locale = locale;
66+
}
67+
}

webapp/src/main/java/com/box/l10n/mojito/quartz/QuartzPollableTaskScheduler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ public <I, O> PollableFuture<O> scheduleJobWithCustomTimeout(
8585
* @param expectedSubTaskNumber set on the pollable task
8686
* @param triggerStartDate date at which the job should be started
8787
* @param uniqueId optional id used to generate the job keyname. If not provided the pollable task
88-
* id is used. Pollable id keeps changing, unique id can be used for recurring jobs (eg. update
89-
* stats of repository xyz)
88+
* id is used. Pollable id keeps changing, unique id can be used for recurring jobs (eg.
89+
* update stats of repository xyz)
9090
* @param inlineInput to inline the input in quartz data or save it in the blobstorage
9191
* @param <I>
9292
* @param <O>

webapp/src/main/java/com/box/l10n/mojito/react/ReactAppController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.box.l10n.mojito.react;
22

33
import com.box.l10n.mojito.entity.security.user.Authority;
4+
import com.box.l10n.mojito.entity.security.user.UserLocale;
45
import com.box.l10n.mojito.json.ObjectMapper;
56
import com.box.l10n.mojito.mustache.MustacheBaseContext;
67
import com.box.l10n.mojito.mustache.MustacheTemplateEngine;
@@ -15,6 +16,7 @@
1516
import java.nio.charset.StandardCharsets;
1617
import java.util.IllformedLocaleException;
1718
import java.util.Locale;
19+
import java.util.stream.Collectors;
1820
import org.slf4j.Logger;
1921
import org.slf4j.LoggerFactory;
2022
import org.springframework.beans.factory.annotation.Autowired;
@@ -134,6 +136,12 @@ ReactUser getReactUser() {
134136
reactUser.setGivenName(currentAuditor.getGivenName());
135137
reactUser.setSurname(currentAuditor.getSurname());
136138
reactUser.setCommonName(currentAuditor.getCommonName());
139+
reactUser.setCanTranslateAllLocales(currentAuditor.getCanTranslateAllLocales());
140+
reactUser.setUserLocales(
141+
currentAuditor.getUserLocales().stream()
142+
.map(UserLocale::getLocale)
143+
.map(com.box.l10n.mojito.entity.Locale::getBcp47Tag)
144+
.collect(Collectors.toList()));
137145

138146
Role role = Role.ROLE_USER;
139147
Authority authority = authorityRepository.findByUser(currentAuditor);

webapp/src/main/java/com/box/l10n/mojito/react/ReactUser.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.box.l10n.mojito.react;
22

33
import com.box.l10n.mojito.security.Role;
4+
import java.util.List;
45
import org.springframework.stereotype.Component;
56

67
@Component
@@ -11,6 +12,8 @@ public class ReactUser {
1112
String surname;
1213
String commonName;
1314
Role role;
15+
boolean canTranslateAllLocales;
16+
List<String> userLocales;
1417

1518
public String getUsername() {
1619
return username;
@@ -51,4 +54,20 @@ public Role getRole() {
5154
public void setRole(Role role) {
5255
this.role = role;
5356
}
57+
58+
public boolean getCanTranslateAllLocales() {
59+
return canTranslateAllLocales;
60+
}
61+
62+
public void setCanTranslateAllLocales(boolean canTranslateAllLocales) {
63+
this.canTranslateAllLocales = canTranslateAllLocales;
64+
}
65+
66+
public List<String> getUserLocales() {
67+
return userLocales;
68+
}
69+
70+
public void setUserLocales(List<String> userLocales) {
71+
this.userLocales = userLocales;
72+
}
5473
}

0 commit comments

Comments
 (0)