Skip to content

Commit 09cbdd6

Browse files
authored
Merge pull request #10 from foxocorp/dev
Merge dev
2 parents 2de95b5 + aa42bac commit 09cbdd6

17 files changed

+133
-62
lines changed

foxogram-api/src/main/java/su/foxogram/controllers/MessagesController.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,7 @@ public List<MessageDTO> getMessages(@RequestAttribute(value = AttributesConstant
5555
@Operation(summary = "Get message")
5656
@GetMapping("/channel/{id}/{messageId}")
5757
public MessageDTO getMessage(@RequestAttribute(value = AttributesConstants.CHANNEL) Channel channel, @PathVariable long id, @PathVariable long messageId) throws MessageNotFoundException {
58-
Message message = messagesService.getMessage(messageId, channel);
59-
60-
return new MessageDTO(message);
58+
return messagesService.getMessage(messageId, channel);
6159
}
6260

6361
@Operation(summary = "Create message")
@@ -69,7 +67,7 @@ public MessageDTO createMessage(@RequestAttribute(value = AttributesConstants.US
6967

7068
Message message = messagesService.addMessage(channel, user, body);
7169

72-
return new MessageDTO(message);
70+
return new MessageDTO(message, null);
7371
}
7472

7573
@Operation(summary = "Delete message")

foxogram-common/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ dependencies {
2020
implementation 'org.mindrot:jbcrypt:0.4'
2121
implementation 'org.slf4j:slf4j-api:2.0.16'
2222
implementation 'ch.qos.logback:logback-classic:1.5.15'
23-
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
24-
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
25-
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'
23+
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
24+
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
25+
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
2626
implementation 'org.apache.commons:commons-imaging:1.0.0-alpha5'
2727
implementation 'io.minio:minio:8.5.14'
2828
implementation 'org.springframework.boot:spring-boot-starter-amqp'

foxogram-common/src/main/java/su/foxogram/dtos/api/response/ChannelDTO.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public ChannelDTO(Channel channel, Message lastMessage) {
4141
this.memberCount = channel.getMembers().size();
4242
}
4343
if (lastMessage != null) {
44-
this.lastMessage = new MessageDTO(lastMessage);
44+
this.lastMessage = new MessageDTO(lastMessage, null);
4545
}
4646
this.owner = new UserDTO(channel.getOwner(), null, false, false);
4747
this.createdAt = channel.getCreatedAt();

foxogram-common/src/main/java/su/foxogram/dtos/api/response/MessageDTO.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.swagger.v3.oas.annotations.media.Schema;
44
import lombok.Getter;
55
import lombok.Setter;
6+
import su.foxogram.models.Attachment;
67
import su.foxogram.models.Message;
78

89
import java.util.List;
@@ -20,16 +21,17 @@ public class MessageDTO {
2021

2122
private ChannelDTO channel;
2223

23-
private List<String> attachments;
24+
private List<?> attachments;
2425

2526
private long createdAt;
2627

27-
public MessageDTO(Message message) {
28+
public MessageDTO(Message message, List<Attachment> attachments) {
2829
this.id = message.getId();
2930
this.content = message.getContent();
3031
this.author = new MemberDTO(message.getAuthor(), false);
3132
this.channel = new ChannelDTO(message.getChannel(), null);
32-
this.attachments = message.getAttachments();
33+
if (attachments != null) this.attachments = attachments;
34+
else this.attachments = message.getAttachments();
3335
this.createdAt = message.getTimestamp();
3436
}
3537
}

foxogram-common/src/main/java/su/foxogram/dtos/api/response/MessagesDTO.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class MessagesDTO {
1616

1717
public MessagesDTO(List<Message> messages) {
1818
for (Message message : messages) {
19-
this.messages.add(new MessageDTO(message));
19+
this.messages.add(new MessageDTO(message, null));
2020
}
2121
}
2222
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package su.foxogram.models;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
@Entity
8+
@Getter
9+
@Setter
10+
@Table(name = "attachments", indexes = {
11+
@Index(name = "idx_attachment_id", columnList = "id", unique = true)
12+
})
13+
public class Attachment {
14+
15+
@Id
16+
public String id;
17+
18+
@Column()
19+
public String filename;
20+
21+
@Column()
22+
public String contentType;
23+
24+
@Column()
25+
public long flags;
26+
27+
public Attachment() {
28+
}
29+
30+
public Attachment(String id, String filename, String contentType, long flags) {
31+
this.id = id;
32+
this.filename = filename;
33+
this.contentType = contentType;
34+
this.flags = flags;
35+
}
36+
}

foxogram-common/src/main/java/su/foxogram/models/Message.java

-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public class Message {
2727
@Column()
2828
public long timestamp;
2929

30-
@ElementCollection(targetClass = String.class, fetch = FetchType.EAGER)
31-
@CollectionTable(name = "attachments", joinColumns = @JoinColumn(name = "message_id"))
3230
@Column()
3331
public List<String> attachments;
3432

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package su.foxogram.repositories;
2+
3+
import jakarta.validation.constraints.NotNull;
4+
import org.springframework.data.repository.CrudRepository;
5+
import org.springframework.stereotype.Repository;
6+
import su.foxogram.models.Attachment;
7+
8+
@Repository
9+
public interface AttachmentRepository extends CrudRepository<Attachment, Long> {
10+
11+
Attachment findById(String id);
12+
13+
@Override
14+
void delete(@NotNull Attachment attachment);
15+
}

foxogram-common/src/main/java/su/foxogram/services/AuthenticationService.java

+19-13
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import su.foxogram.repositories.CodeRepository;
2626
import su.foxogram.repositories.UserRepository;
2727
import su.foxogram.util.CodeGenerator;
28-
import su.foxogram.util.Encryptor;
28+
import su.foxogram.util.PasswordHasher;
2929

3030
@Slf4j
3131
@Service
@@ -54,24 +54,30 @@ public AuthenticationService(UserRepository userRepository, CodeRepository codeR
5454

5555
public User getUser(String header, boolean ignoreEmailVerification, boolean ignoreBearer) throws UserUnauthorizedException, UserEmailNotVerifiedException {
5656
long userId;
57+
String passwordHash;
5758

5859
try {
59-
String claims = header.substring(7);
60+
String token = header.substring(7);
6061

61-
if (ignoreBearer) claims = header;
62+
if (ignoreBearer) token = header;
6263

63-
Jws<Claims> claimsJws = Jwts.parserBuilder()
64-
.setSigningKey(jwtService.getSigningKey())
64+
Jws<Claims> claimsJws = Jwts.parser()
65+
.verifyWith(jwtService.getSigningKey())
6566
.build()
66-
.parseClaimsJws(claims);
67+
.parseSignedClaims(token);
6768

68-
userId = Long.parseLong(claimsJws.getBody().getId());
69+
userId = Long.parseLong(claimsJws.getPayload().getId());
70+
passwordHash = claimsJws.getPayload().getSubject();
6971
} catch (Exception e) {
7072
throw new UserUnauthorizedException();
7173
}
7274

7375
User user = userRepository.findById(userId).orElseThrow(UserUnauthorizedException::new);
7476

77+
if (!user.getPassword().equals(passwordHash)) {
78+
throw new UserUnauthorizedException();
79+
}
80+
7581
if (!ignoreEmailVerification && user.hasFlag(UserConstants.Flags.EMAIL_VERIFIED))
7682
throw new UserEmailNotVerifiedException();
7783

@@ -92,23 +98,23 @@ public String userRegister(String username, String email, String password) throw
9298

9399
log.info("User ({}, {}) email verification message sent successfully", user.getUsername(), user.getEmail());
94100

95-
return jwtService.generate(user.getId());
101+
return jwtService.generate(user.getId(), user.getPassword());
96102
}
97103

98104
private User createUser(String username, String email, String password) {
99105
long deletion = 0;
100106
long flags = UserConstants.Flags.AWAITING_CONFIRMATION.getBit();
101107
int type = UserConstants.Type.USER.getType();
102108

103-
return new User(0, null, null, username, email, Encryptor.hashPassword(password), flags, type, deletion, null);
109+
return new User(0, null, null, username, email, PasswordHasher.hashPassword(password), flags, type, deletion, null);
104110
}
105111

106112
private void sendConfirmationEmail(User user) {
107113
String emailType = EmailConstants.Type.EMAIL_VERIFY.getValue();
108114
String digitCode = CodeGenerator.generateDigitCode();
109115
long issuedAt = System.currentTimeMillis();
110116
long expiresAt = issuedAt + CodesConstants.Lifetime.BASE.getValue();
111-
String accessToken = jwtService.generate(user.getId());
117+
String accessToken = jwtService.generate(user.getId(), user.getPassword());
112118

113119
emailService.sendEmail(user.getEmail(), user.getId(), emailType, user.getUsername(), digitCode, issuedAt, expiresAt, accessToken);
114120
}
@@ -118,15 +124,15 @@ public String loginUser(String email, String password) throws UserCredentialsIsI
118124
validatePassword(user, password);
119125

120126
log.info("User ({}, {}) login successfully", user.getUsername(), user.getEmail());
121-
return jwtService.generate(user.getId());
127+
return jwtService.generate(user.getId(), user.getPassword());
122128
}
123129

124130
public User findUserByEmail(String email) throws UserCredentialsIsInvalidException {
125131
return userRepository.findByEmail(email).orElseThrow(UserCredentialsIsInvalidException::new);
126132
}
127133

128134
private void validatePassword(User user, String password) throws UserCredentialsIsInvalidException {
129-
if (!Encryptor.verifyPassword(password, user.getPassword()))
135+
if (!PasswordHasher.verifyPassword(password, user.getPassword()))
130136
throw new UserCredentialsIsInvalidException();
131137
}
132138

@@ -176,7 +182,7 @@ public void confirmResetPassword(UserResetPasswordConfirmDTO body) throws CodeEx
176182
User user = userRepository.findByEmail(body.getEmail()).orElseThrow(UserCredentialsIsInvalidException::new);
177183
Code code = codeService.validateCode(body.getCode());
178184

179-
user.setPassword(Encryptor.hashPassword(body.getNewPassword()));
185+
user.setPassword(PasswordHasher.hashPassword(body.getNewPassword()));
180186
user.removeFlag(UserConstants.Flags.AWAITING_CONFIRMATION);
181187

182188
codeService.deleteCode(code);

foxogram-common/src/main/java/su/foxogram/services/ChannelsService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private void changeIcon(Channel channel, MultipartFile icon) throws UploadFailed
142142
String hash;
143143

144144
try {
145-
hash = storageService.uploadToMinio(icon, StorageConstants.AVATARS_BUCKET);
145+
hash = storageService.uploadIdentityImage(icon, StorageConstants.AVATARS_BUCKET);
146146
} catch (Exception e) {
147147
throw new UploadFailedException();
148148
}

foxogram-common/src/main/java/su/foxogram/services/EmailService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import su.foxogram.configs.APIConfig;
1414
import su.foxogram.configs.EmailConfig;
1515
import su.foxogram.constants.EmailConstants;
16-
import su.foxogram.util.Algorithm;
16+
import su.foxogram.util.StringUtils;
1717

1818
import java.io.IOException;
1919
import java.io.InputStream;
@@ -92,7 +92,7 @@ private String readHTML() throws IOException {
9292
}
9393

9494
try (InputStream inputStream = resource.getInputStream()) {
95-
return Algorithm.inputStreamToString(inputStream, StandardCharsets.UTF_8);
95+
return StringUtils.inputStreamToString(inputStream, StandardCharsets.UTF_8);
9696
}
9797
}
9898
}

foxogram-common/src/main/java/su/foxogram/services/JwtService.java

+7-7
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import su.foxogram.configs.JwtConfig;
99
import su.foxogram.constants.TokenConstants;
1010

11-
import java.security.Key;
11+
import javax.crypto.SecretKey;
1212
import java.util.Date;
1313

1414
@Service
@@ -20,19 +20,19 @@ public JwtService(JwtConfig jwtConfig) {
2020
this.jwtConfig = jwtConfig;
2121
}
2222

23-
public String generate(long id) {
23+
public String generate(long id, String passwordHash) {
2424
long now = System.currentTimeMillis();
2525
Date expirationDate = new Date(now + TokenConstants.LIFETIME);
2626

2727
return Jwts.builder()
28-
.setId(String.valueOf(id))
29-
.setExpiration(expirationDate)
28+
.id(String.valueOf(id))
29+
.subject(passwordHash)
30+
.expiration(expirationDate)
3031
.signWith(getSigningKey())
3132
.compact();
3233
}
3334

34-
public Key getSigningKey() {
35-
byte[] keyBytes = Decoders.BASE64.decode(jwtConfig.getSecret());
36-
return Keys.hmacShaKeyFor(keyBytes);
35+
public SecretKey getSigningKey() {
36+
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtConfig.getSecret()));
3737
}
3838
}

foxogram-common/src/main/java/su/foxogram/services/MessagesService.java

+22-12
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
import su.foxogram.exceptions.cdn.UploadFailedException;
1414
import su.foxogram.exceptions.member.MissingPermissionsException;
1515
import su.foxogram.exceptions.message.MessageNotFoundException;
16-
import su.foxogram.models.Channel;
17-
import su.foxogram.models.Member;
18-
import su.foxogram.models.Message;
19-
import su.foxogram.models.User;
16+
import su.foxogram.models.*;
17+
import su.foxogram.repositories.AttachmentRepository;
2018
import su.foxogram.repositories.ChannelRepository;
2119
import su.foxogram.repositories.MemberRepository;
2220
import su.foxogram.repositories.MessageRepository;
@@ -40,13 +38,16 @@ public class MessagesService {
4038

4139
private final MemberRepository memberRepository;
4240

41+
private final AttachmentRepository attachmentRepository;
42+
4343
@Autowired
44-
public MessagesService(MessageRepository messageRepository, StorageService storageService, RabbitService rabbitService, ChannelRepository channelRepository, MemberRepository memberRepository) {
44+
public MessagesService(MessageRepository messageRepository, StorageService storageService, RabbitService rabbitService, ChannelRepository channelRepository, MemberRepository memberRepository, AttachmentRepository attachmentRepository) {
4545
this.messageRepository = messageRepository;
4646
this.storageService = storageService;
4747
this.rabbitService = rabbitService;
4848
this.channelRepository = channelRepository;
4949
this.memberRepository = memberRepository;
50+
this.attachmentRepository = attachmentRepository;
5051
}
5152

5253
public List<MessageDTO> getMessages(long before, int limit, Channel channel) {
@@ -55,18 +56,27 @@ public List<MessageDTO> getMessages(long before, int limit, Channel channel) {
5556
log.info("Messages ({}, {}) in channel ({}) found successfully", limit, before, channel.getId());
5657

5758
return messagesArray.reversed().stream()
58-
.map(MessageDTO::new)
59+
.map(message -> {
60+
List<Attachment> attachments = new ArrayList<>();
61+
if (message.getAttachments() != null) {
62+
message.getAttachments().forEach(attachment -> attachments.add(attachmentRepository.findById(attachment)));
63+
}
64+
return new MessageDTO(message, attachments);
65+
})
5966
.collect(Collectors.toList());
6067
}
6168

62-
public Message getMessage(long id, Channel channel) throws MessageNotFoundException {
69+
public MessageDTO getMessage(long id, Channel channel) throws MessageNotFoundException {
6370
Message message = messageRepository.findByChannelAndId(channel, id);
6471

6572
if (message == null) throw new MessageNotFoundException();
6673

74+
List<Attachment> attachments = new ArrayList<>();
75+
message.getAttachments().forEach(attachment -> attachments.add(attachmentRepository.findById(attachment)));
76+
6777
log.info("Message ({}) in channel ({}) found successfully", id, channel.getId());
6878

69-
return message;
79+
return new MessageDTO(message, attachments);
7080
}
7181

7282
public Message addMessage(Channel channel, User user, MessageCreateDTO body) throws UploadFailedException, JsonProcessingException {
@@ -78,7 +88,7 @@ public Message addMessage(Channel channel, User user, MessageCreateDTO body) thr
7888
uploadedAttachments = body.getAttachments().stream()
7989
.map(attachment -> {
8090
try {
81-
return uploadAttachment(attachment);
91+
return uploadAttachment(attachment).getId();
8292
} catch (UploadFailedException e) {
8393
throw new RuntimeException(e);
8494
}
@@ -92,7 +102,7 @@ public Message addMessage(Channel channel, User user, MessageCreateDTO body) thr
92102
Message message = new Message(channel, body.getContent(), member, uploadedAttachments);
93103
messageRepository.save(message);
94104

95-
rabbitService.send(getRecipients(channel), new MessageDTO(message), GatewayConstants.Event.MESSAGE_CREATE.getValue());
105+
rabbitService.send(getRecipients(channel), new MessageDTO(message, null), GatewayConstants.Event.MESSAGE_CREATE.getValue());
96106
log.info("Message ({}) to channel ({}) created successfully", message.getId(), channel.getId());
97107

98108
return message;
@@ -120,13 +130,13 @@ public Message editMessage(long id, Channel channel, Member member, MessageCreat
120130
message.setContent(content);
121131
messageRepository.save(message);
122132

123-
rabbitService.send(getRecipients(channel), new MessageDTO(message), GatewayConstants.Event.MESSAGE_UPDATE.getValue());
133+
rabbitService.send(getRecipients(channel), new MessageDTO(message, null), GatewayConstants.Event.MESSAGE_UPDATE.getValue());
124134
log.info("Message ({}) in channel ({}) edited successfully", id, channel.getId());
125135

126136
return message;
127137
}
128138

129-
private String uploadAttachment(MultipartFile attachment) throws UploadFailedException {
139+
private Attachment uploadAttachment(MultipartFile attachment) throws UploadFailedException {
130140
try {
131141
return storageService.uploadToMinio(attachment, StorageConstants.ATTACHMENTS_BUCKET);
132142
} catch (Exception e) {

0 commit comments

Comments
 (0)