Skip to content

Commit 86b87fc

Browse files
authored
Add population of the service links table (#536)
1 parent ed99422 commit 86b87fc

16 files changed

Lines changed: 284 additions & 75 deletions

File tree

src/inttest/java/com/faforever/api/config/MainDbTestContainers.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
@Configuration
2222
public class MainDbTestContainers {
2323
private static final MariaDBContainer<?> fafDBContainer = new MariaDBContainer<>("mariadb:10.6");
24-
private static final GenericContainer<?> flywayMigrationsContainer = new GenericContainer<>("faforever/faf-db-migrations:v125");
24+
private static final GenericContainer<?> flywayMigrationsContainer = new GenericContainer<>("faforever/faf-db-migrations:v126");
2525
private static final Network sharedNetwork = Network.newNetwork();
2626

2727
@Bean

src/inttest/java/com/faforever/api/data/PlayerElideTest.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,6 @@ public void restrictedResultWithoutScope() throws Exception {
4040
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("email"))))
4141
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("email"))))
4242
.andExpect(jsonPath("$.data[3].attributes", not(hasKey("email"))))
43-
.andExpect(jsonPath("$.data[0].attributes", not(hasKey("steamId"))))
44-
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("steamId"))))
45-
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("steamId"))))
46-
.andExpect(jsonPath("$.data[3].attributes", not(hasKey("steamId"))))
4743
.andExpect(jsonPath("$.data[0].attributes", not(hasKey("recentIpAddress"))))
4844
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("recentIpAddress"))))
4945
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("recentIpAddress"))))
@@ -59,7 +55,6 @@ public void restrictedResultWithoutScope() throws Exception {
5955
// you are allowed to see your own stuff
6056
.andExpect(jsonPath("$.data[4].attributes.email", is("active-user@faforever.com")))
6157
.andExpect(jsonPath("$.data[4].attributes.recentIpAddress", is("127.0.0.1")))
62-
.andExpect(jsonPath("$.data[4].attributes", hasKey("steamId")))
6358
.andExpect(jsonPath("$.data[4].attributes", hasKey("lastLogin")))
6459
// you cannot see your uuid
6560
.andExpect(jsonPath("$.data[4].relationships", not(hasKey("uniqueIds"))))
@@ -97,10 +92,6 @@ public void restrictedResultWithoutRole() throws Exception {
9792
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("email"))))
9893
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("email"))))
9994
.andExpect(jsonPath("$.data[3].attributes", not(hasKey("email"))))
100-
.andExpect(jsonPath("$.data[0].attributes", not(hasKey("steamId"))))
101-
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("steamId"))))
102-
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("steamId"))))
103-
.andExpect(jsonPath("$.data[3].attributes", not(hasKey("steamId"))))
10495
.andExpect(jsonPath("$.data[0].attributes", not(hasKey("recentIpAddress"))))
10596
.andExpect(jsonPath("$.data[1].attributes", not(hasKey("recentIpAddress"))))
10697
.andExpect(jsonPath("$.data[2].attributes", not(hasKey("recentIpAddress"))))
@@ -116,7 +107,6 @@ public void restrictedResultWithoutRole() throws Exception {
116107
// you are allowed to see your own stuff
117108
.andExpect(jsonPath("$.data[4].attributes.email", is("active-user@faforever.com")))
118109
.andExpect(jsonPath("$.data[4].attributes.recentIpAddress", is("127.0.0.1")))
119-
.andExpect(jsonPath("$.data[4].attributes", hasKey("steamId")))
120110
.andExpect(jsonPath("$.data[4].attributes", hasKey("lastLogin")))
121111
.andExpect(jsonPath("$.data[4].relationships", hasKey("reporterOnModerationReports")))
122112
.andExpect(jsonPath("$.data[4].relationships", hasKey("userGroups")))
@@ -167,11 +157,6 @@ public void canSeePrivateDetailsWithScopeAndRole() throws Exception {
167157
.andExpect(jsonPath("$.data[2].attributes", hasKey("lastLogin")))
168158
.andExpect(jsonPath("$.data[3].attributes", hasKey("lastLogin")))
169159
.andExpect(jsonPath("$.data[4].attributes", hasKey("lastLogin")))
170-
.andExpect(jsonPath("$.data[0].attributes", hasKey("steamId")))
171-
.andExpect(jsonPath("$.data[1].attributes", hasKey("steamId")))
172-
.andExpect(jsonPath("$.data[2].attributes", hasKey("steamId")))
173-
.andExpect(jsonPath("$.data[3].attributes", hasKey("steamId")))
174-
.andExpect(jsonPath("$.data[4].attributes", hasKey("steamId")))
175160
.andExpect(jsonPath("$.data[0].relationships", hasKey("uniqueIds")))
176161
.andExpect(jsonPath("$.data[1].relationships", hasKey("uniqueIds")))
177162
.andExpect(jsonPath("$.data[2].relationships", hasKey("uniqueIds")))

src/inttest/java/com/faforever/api/user/UsersControllerTest.java

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import com.faforever.api.AbstractIntegrationTest;
55
import com.faforever.api.data.domain.GroupPermission;
6+
import com.faforever.api.data.domain.LinkedServiceType;
67
import com.faforever.api.data.domain.User;
78
import com.faforever.api.email.EmailSender;
89
import com.faforever.api.error.ErrorCode;
@@ -24,7 +25,6 @@
2425
import static org.hamcrest.MatcherAssert.assertThat;
2526
import static org.hamcrest.Matchers.is;
2627
import static org.hamcrest.Matchers.not;
27-
import static org.hamcrest.Matchers.nullValue;
2828
import static org.mockito.ArgumentMatchers.any;
2929
import static org.mockito.ArgumentMatchers.anyString;
3030
import static org.mockito.ArgumentMatchers.eq;
@@ -61,6 +61,9 @@ public class UsersControllerTest extends AbstractIntegrationTest {
6161
@Autowired
6262
private UserRepository userRepository;
6363

64+
@Autowired
65+
private AccountLinkRepository accountLinkRepository;
66+
6467
@Test
6568
public void registerWithSuccess() throws Exception {
6669
MultiValueMap<String, String> params = new HttpHeaders();
@@ -292,9 +295,10 @@ public void buildSteamLinkUrl() throws Exception {
292295

293296
@Test
294297
public void linkToSteam() throws Exception {
295-
assertThat(userRepository.getById(1).getSteamId(), nullValue());
296-
297298
String steamId = "12345";
299+
300+
assertThat(accountLinkRepository.findOneByServiceIdAndServiceType(steamId, LinkedServiceType.STEAM).isEmpty(), is(true));
301+
298302
String callbackUrl = "http://faforever.com";
299303
String token = fafTokenService.createToken(
300304
FafTokenType.LINK_TO_STEAM,
@@ -312,15 +316,14 @@ public void linkToSteam() throws Exception {
312316
.andExpect(status().isFound())
313317
.andExpect(redirectedUrl(callbackUrl));
314318

315-
assertThat(userRepository.getById(1).getSteamId(), is(steamId));
319+
assertThat(accountLinkRepository.findOneByServiceIdAndServiceType(steamId, LinkedServiceType.STEAM).get().getUser().getId(), is(1));
316320
}
317321

318322
@Test
319323
public void linkToSteamAlreadyLinkedAccount() throws Exception {
320324
String steamId = "1234";
321-
assertThat(userRepository.getById(1).getSteamId(), nullValue());
322-
User userThatOwnsSteamId = userRepository.getById(2);
323-
assertThat(userThatOwnsSteamId.getSteamId(), is(steamId));
325+
User userThatOwnsSteamId = accountLinkRepository.findOneByServiceIdAndServiceType(steamId, LinkedServiceType.STEAM).get().getUser();
326+
assertThat(userThatOwnsSteamId.getId(), is(2));
324327

325328
String callbackUrl = "http://faforever.com";
326329
String token = fafTokenService.createToken(
@@ -340,7 +343,7 @@ public void linkToSteamAlreadyLinkedAccount() throws Exception {
340343
.andExpect(redirectedUrlPattern(callbackUrl + "?errors=*" + ErrorCode.STEAM_ID_ALREADY_LINKED.getCode() + "*" + userThatOwnsSteamId.getLogin() + "*"));
341344
//We expect and error with code STEAM_ID_ALREADY_LINKED and that the error message contains the user that this steam account was linked to already which is MODERATOR with id 2
342345

343-
assertThat(userRepository.getById(1).getSteamId(), nullValue());
346+
assertThat(accountLinkRepository.existsByUserAndServiceType(userRepository.getReferenceById(1), LinkedServiceType.STEAM), is(false));
344347
}
345348

346349
@Test
@@ -508,7 +511,7 @@ public void linkToGogWithoutOAuthScopeFails() throws Exception {
508511
}
509512

510513
@Test
511-
public void linkToGogWithoutSuccess() throws Exception {
514+
public void linkToGogSuccess() throws Exception {
512515
MultiValueMap<String, String> params = new HttpHeaders();
513516
params.add("gogUsername", "someUsername");
514517

@@ -519,5 +522,23 @@ public void linkToGogWithoutSuccess() throws Exception {
519522
.with(getOAuthTokenForUserId(USERID_USER, OAuthScope._WRITE_ACCOUNT_DATA))
520523
.params(params))
521524
.andExpect(status().isOk());
525+
526+
assertThat(accountLinkRepository.existsByUserAndServiceType(userRepository.getReferenceById(1), LinkedServiceType.GOG), is(true));
527+
}
528+
529+
@Test
530+
public void linkToGogAlreadyLinked() throws Exception {
531+
MultiValueMap<String, String> params = new HttpHeaders();
532+
params.add("gogUsername", "username");
533+
534+
when(gogService.buildGogToken(any())).thenReturn("theToken");
535+
536+
mockMvc.perform(
537+
post("/users/linkToGog")
538+
.with(getOAuthTokenForUserId(USERID_USER, OAuthScope._WRITE_ACCOUNT_DATA))
539+
.params(params))
540+
.andExpect(status().isUnprocessableEntity());
541+
542+
assertThat(accountLinkRepository.existsByUserAndServiceType(userRepository.getReferenceById(1), LinkedServiceType.GOG), is(false));
522543
}
523544
}

src/inttest/java/com/faforever/api/utils/OAuthHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public OAuthHelper(PlayerRepository playerRepository) {
2323
}
2424

2525
public RequestPostProcessor addBearerTokenForUser(int userId, @NotNull Set<String> scopes) {
26-
Player user = playerRepository.getById(userId);
26+
Player user = playerRepository.getReferenceById(userId);
2727

2828
Set<FafRole> roles = user.getUserGroups().stream()
2929
.flatMap(userGroup -> userGroup.getPermissions().stream())

src/inttest/resources/sql/prepDefaultData.sql

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,17 @@ VALUES ('test', 'test', '{noop}test', 'public', 'http://localhost https://www.ge
33
'http://localhost',
44
'read_events read_achievements upload_map upload_mod upload_avatar write_account_data vote read_sensible_userdata');
55

6-
INSERT INTO login (id, login, email, password, steamid, ip)
7-
VALUES (1, 'USER', 'user@faforever.com', '92b7b421992ef490f3b75898ec0e511f1a5c02422819d89719b20362b023ee4f', NULL,
6+
INSERT INTO login (id, login, email, password, ip)
7+
VALUES (1, 'USER', 'user@faforever.com', '92b7b421992ef490f3b75898ec0e511f1a5c02422819d89719b20362b023ee4f',
88
'127.0.0.1'),
9-
(2, 'MODERATOR', 'moderator@faforever.com', '778ac5b81fa251b450f827846378739caee510c31b01cfa9d31822b88bed8441',
10-
1234, '127.0.0.1'),
11-
(3, 'ADMIN', 'admin@faforever.com', '835d6dc88b708bc646d6db82c853ef4182fabbd4a8de59c213f2b5ab3ae7d9be', NULL,
12-
'127.0.0.1'),
13-
(4, 'BANNED', 'banned@faforever.com', '', NULL, '127.0.0.1'),
14-
(5, 'ACTIVE_USER', 'active-user@faforever.com', '', null, '127.0.0.1');
9+
(2, 'MODERATOR', 'moderator@faforever.com', '778ac5b81fa251b450f827846378739caee510c31b01cfa9d31822b88bed8441', '127.0.0.1'),
10+
(3, 'ADMIN', 'admin@faforever.com', '835d6dc88b708bc646d6db82c853ef4182fabbd4a8de59c213f2b5ab3ae7d9be', '127.0.0.1'),
11+
(4, 'BANNED', 'banned@faforever.com', '', '127.0.0.1'),
12+
(5, 'ACTIVE_USER', 'active-user@faforever.com', '', '127.0.0.1');
13+
14+
INSERT INTO service_links (id, user_id, service_id, type, public, ownership)
15+
VALUES (UUID(), 2, '1234', 'STEAM', false, true),
16+
(UUID(), 2, 'username', 'GOG', false, true);
1517

1618
INSERT INTO user_group (id, technical_name, name_key, parent_group_id, public)
1719
VALUES (1, 'ADMINISTRATOR', 'administrator', null, true),

src/inttest/resources/sql/truncateTables.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ DELETE FROM `patchs_table`;
5959
DELETE FROM `player_achievements`;
6060
DELETE FROM `player_events`;
6161
DELETE FROM `reported_user`;
62+
DELETE FROM `service_links`;
6263
DELETE FROM `table_map_comments`;
6364
DELETE FROM `teamkills`;
6465
DELETE FROM `tutorial`;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.faforever.api.data.domain;
2+
3+
import com.faforever.api.data.checks.IsEntityOwner;
4+
import com.faforever.api.data.checks.Prefab;
5+
import com.faforever.api.security.elide.permission.ReadAccountPrivateDetailsCheck;
6+
import com.fasterxml.jackson.annotation.JsonIgnore;
7+
import com.yahoo.elide.annotation.Include;
8+
import com.yahoo.elide.annotation.ReadPermission;
9+
import com.yahoo.elide.annotation.UpdatePermission;
10+
import lombok.Setter;
11+
12+
import javax.persistence.Column;
13+
import javax.persistence.Entity;
14+
import javax.persistence.EnumType;
15+
import javax.persistence.Enumerated;
16+
import javax.persistence.FetchType;
17+
import javax.persistence.Id;
18+
import javax.persistence.JoinColumn;
19+
import javax.persistence.ManyToOne;
20+
import javax.persistence.Table;
21+
import javax.persistence.Transient;
22+
23+
@Entity
24+
@Table(name = "service_links")
25+
@Include(name = "accountLink")
26+
@Setter
27+
@ReadPermission(expression = IsEntityOwner.EXPRESSION + " OR " + ReadAccountPrivateDetailsCheck.EXPRESSION)
28+
public class AccountLink implements OwnableEntity {
29+
30+
private String id;
31+
private User user;
32+
private LinkedServiceType serviceType;
33+
private String serviceId;
34+
private boolean public_;
35+
private boolean ownership;
36+
37+
@Id
38+
@Column(name = "id")
39+
public String getId() {
40+
return id;
41+
}
42+
43+
@Column(name = "type")
44+
@Enumerated(EnumType.STRING)
45+
public LinkedServiceType getServiceType() {
46+
return serviceType;
47+
}
48+
49+
@ManyToOne(fetch = FetchType.LAZY)
50+
@JoinColumn(name = "user_id")
51+
public User getUser() {
52+
return user;
53+
}
54+
55+
@Column(name = "service_id")
56+
@UpdatePermission(expression = Prefab.NONE)
57+
public String getServiceId() {
58+
return serviceId;
59+
}
60+
61+
@Column(name = "public")
62+
@ReadPermission(expression = ReadAccountPrivateDetailsCheck.EXPRESSION)
63+
public boolean getPublic() {
64+
return public_;
65+
}
66+
67+
public AccountLink setPublic(boolean public_) {
68+
this.public_ = public_;
69+
return this;
70+
}
71+
72+
@Column(name = "ownership")
73+
@ReadPermission(expression = ReadAccountPrivateDetailsCheck.EXPRESSION)
74+
public boolean getOwnership() {
75+
return ownership;
76+
}
77+
78+
@Override
79+
@Transient
80+
@JsonIgnore
81+
public Login getEntityOwner() {
82+
return user;
83+
}
84+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.faforever.api.data.domain;
2+
3+
public enum LinkedServiceType {
4+
STEAM, GOG, DISCORD, PATREON;
5+
}

src/main/java/com/faforever/api/data/domain/Login.java

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ public abstract class Login extends AbstractEntity<Login> implements OwnableEnti
3232

3333
private String login;
3434
private String email;
35-
private String steamId;
36-
private String gogId;
3735
private String userAgent;
3836
private Set<BanInfo> bans;
37+
private Set<AccountLink> accountLinks;
3938
private Set<UserNote> userNotes;
4039
private Set<UserGroup> userGroups;
4140
private String recentIpAddress;
@@ -59,18 +58,6 @@ public String getEmail() {
5958
return email;
6059
}
6160

62-
@Column(name = "steamid")
63-
@ReadPermission(expression = IsEntityOwner.EXPRESSION + " OR " + ReadAccountPrivateDetailsCheck.EXPRESSION)
64-
public String getSteamId() {
65-
return steamId;
66-
}
67-
68-
@Column(name = "gog_id")
69-
@ReadPermission(expression = IsEntityOwner.EXPRESSION + " OR " + ReadAccountPrivateDetailsCheck.EXPRESSION)
70-
public String getGogId() {
71-
return gogId;
72-
}
73-
7461
@Column(name = "ip")
7562
@ReadPermission(expression = IsEntityOwner.EXPRESSION + " OR " + ReadAccountPrivateDetailsCheck.EXPRESSION)
7663
public String getRecentIpAddress() {
@@ -103,6 +90,12 @@ public Set<UserNote> getUserNotes() {
10390
return this.userNotes;
10491
}
10592

93+
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
94+
@BatchSize(size = 1000)
95+
public Set<AccountLink> getAccountLinks() {
96+
return this.accountLinks;
97+
}
98+
10699
@Transient
107100
public Set<BanInfo> getActiveBans() {
108101
return getBans().stream().filter(ban -> ban.getBanStatus() == BanStatus.BANNED).collect(Collectors.toSet());
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.faforever.api.user;
2+
3+
import com.faforever.api.data.domain.AccountLink;
4+
import com.faforever.api.data.domain.LinkedServiceType;
5+
import com.faforever.api.data.domain.Login;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
import org.springframework.stereotype.Repository;
8+
9+
import java.util.Optional;
10+
11+
@Repository
12+
public interface AccountLinkRepository extends JpaRepository<AccountLink, String> {
13+
Optional<AccountLink> findOneByServiceIdAndServiceType(String serviceId, LinkedServiceType serviceType);
14+
15+
boolean existsByUserAndServiceType(Login login, LinkedServiceType serviceType);
16+
}

0 commit comments

Comments
 (0)