Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ services:
ports:
- "8585:8585"
- "5005:5005"
image: maven:3.9.9-eclipse-temurin-25
image: maven:3.9.12-eclipse-temurin-25
entrypoint: './mvnw -ntp spring-boot:run -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005" -Dmaven.plugin.validation=VERBOSE'
working_dir: /app
volumes:
Expand Down
2 changes: 1 addition & 1 deletion server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@
<configuration>
<enableLazyInitialization>true</enableLazyInitialization>
<enableDirtyTracking>true</enableDirtyTracking>
<enableAssociationManagement>true</enableAssociationManagement>
<enableAssociationManagement>false</enableAssociationManagement>
</configuration>
</execution>
</executions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
Expand Down Expand Up @@ -59,6 +60,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.permitAll())
.httpBasic(AbstractHttpConfigurer::disable)
.formLogin(AbstractHttpConfigurer::disable)
.sessionManagement(
session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(
exceptionHandling ->
exceptionHandling.authenticationEntryPoint(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package br.com.tasknoteapp.server.controller;

import br.com.tasknoteapp.server.entity.NoteEntity;
import br.com.tasknoteapp.server.exception.NoteNotFoundException;
import br.com.tasknoteapp.server.request.NotePatchRequest;
import br.com.tasknoteapp.server.request.NoteRequest;
Expand Down Expand Up @@ -77,8 +76,8 @@ public ResponseEntity<NoteResponse> patchNote(
*/
@PostMapping
public ResponseEntity<NoteResponse> postNotes(@RequestBody @Valid NoteRequest noteRequest) {
NoteEntity createdNote = noteService.createNote(noteRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(NoteResponse.fromEntity(createdNote));
NoteResponse createdNote = noteService.createNote(noteRequest);
return ResponseEntity.status(HttpStatus.CREATED).body(createdNote);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;

Expand All @@ -30,9 +29,6 @@ public class NoteEntity {
@ManyToOne(fetch = FetchType.LAZY)
private UserEntity user;

@OneToOne(mappedBy = "note", fetch = FetchType.LAZY)
private NoteUrlEntity noteUrl;

@Column(name = "tag", nullable = true, length = 30)
private String tag;

Expand Down Expand Up @@ -77,14 +73,6 @@ public void setUser(UserEntity user) {
this.user = user;
}

public NoteUrlEntity getNoteUrl() {
return noteUrl;
}

public void setNoteUrl(NoteUrlEntity noteUrl) {
this.noteUrl = noteUrl;
}

public String getTag() {
return tag;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.Collection;
Expand Down Expand Up @@ -41,9 +40,6 @@ public class UserEntity implements UserDetails {
@Column(name = "name", length = 20)
private String name;

@OneToMany(mappedBy = "user")
private List<TaskEntity> tasks;

@Column(name = "email_confirmed_at", nullable = true)
private LocalDateTime emailConfirmedAt;

Expand Down Expand Up @@ -147,14 +143,6 @@ public void setName(String name) {
this.name = name;
}

public List<TaskEntity> getTasks() {
return tasks;
}

public void setTasks(List<TaskEntity> tasks) {
this.tasks = tasks;
}

public LocalDateTime getEmailConfirmedAt() {
return emailConfirmedAt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public interface NoteRepository extends JpaRepository<NoteEntity, Long> {

Optional<NoteEntity> findByShareToken(String shareToken);

Optional<NoteEntity> findByIdAndUser_id(Long id, Long userId);

@Query(
"select n from NoteEntity n where (upper(n.title) like %?1% or upper(n.description) like"
+ " %?1%) and n.user.id = ?2")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package br.com.tasknoteapp.server.repository;

import br.com.tasknoteapp.server.entity.NoteUrlEntity;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

/** This interface represents a note url repository, for database access. */
public interface NoteUrlRepository extends JpaRepository<NoteUrlEntity, Long> {

Optional<NoteUrlEntity> findByNote_id(Long noteId);

List<NoteUrlEntity> findAllByNote_idIn(List<Long> noteIds);

void deleteByNote_id(Long noteId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface UserPwdLimitRepository extends JpaRepository<UserPwdLimitEntity

List<UserPwdLimitEntity> findAllByUser_id(Long userId, Sort sort);

List<UserPwdLimitEntity> findTop3ByUser_idOrderByWhenHappenedDesc(Long userId);

@Modifying
@Query("delete UserPwdLimitEntity u where u.user.id = ?1")
void deleteAllForUser(Long userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package br.com.tasknoteapp.server.response;

import br.com.tasknoteapp.server.entity.NoteEntity;
import br.com.tasknoteapp.server.entity.NoteUrlEntity;
import br.com.tasknoteapp.server.util.TimeAgoUtil;
import java.util.Objects;

/** This record represents a task and its urls object to be returned. */
public record NoteResponse(
Expand All @@ -16,9 +14,7 @@ public record NoteResponse(
* @param entity The NoteEntity source data.
* @return NoteResponse instance with all note data and urls, if any.
*/
public static NoteResponse fromEntity(NoteEntity entity) {
NoteUrlEntity noteUrl = entity.getNoteUrl();
String url = Objects.isNull(noteUrl) ? null : noteUrl.getUrl();
public static NoteResponse fromEntity(NoteEntity entity, String url) {
String timeAgoFmt = TimeAgoUtil.format(entity.getLastUpdate());

return new NoteResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
Expand Down Expand Up @@ -513,8 +511,10 @@ private Optional<String> getGravatarImageUrl(String email) {
}

private void checkLoginAttemptLimit(Long userId) {
Sort sort = Sort.by(Direction.DESC, "whenHappened");
List<UserPwdLimitEntity> userPwdList = userPwdLimitRepository.findAllByUser_id(userId, sort);
// Fetch only the 3 most recent failed attempts to avoid loading unbounded rows for
// targeted/brute-forced accounts.
List<UserPwdLimitEntity> userPwdList =
userPwdLimitRepository.findTop3ByUser_idOrderByWhenHappenedDesc(userId);

logger.warn("login count attempt for user {}: {}", userId, userPwdList.size());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import br.com.tasknoteapp.server.templates.MailgunTemplateResetPwdConfirm;
import br.com.tasknoteapp.server.templates.MailgunTemplateSignUp;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;
import java.util.Objects;
import org.slf4j.Logger;
Expand Down Expand Up @@ -53,7 +54,11 @@ public MailgunEmailService(
this.senderEmail = sender;
this.targetEnv = targetEnv;
this.restTemplate =
templateBuilder.defaultHeader(HttpHeaders.AUTHORIZATION, basicAuth("api", apiKey)).build();
templateBuilder
.connectTimeout(Duration.ofSeconds(5))
.readTimeout(Duration.ofSeconds(10))
.defaultHeader(HttpHeaders.AUTHORIZATION, basicAuth("api", apiKey))
.build();
}

/**
Expand Down
Loading