Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7a28a0f
create app-button
MarineGiroux Sep 29, 2025
dfa800a
reduce auth.service.ts
MarineGiroux Sep 29, 2025
2bbfe3c
delete base-import.component
MarineGiroux Sep 30, 2025
93c6a6c
reduce setting-team-general-page and setting-team-members-page
MarineGiroux Sep 30, 2025
dc57ea7
reduce customize-event
MarineGiroux Sep 30, 2025
18a4019
reduce session-detail-page
MarineGiroux Oct 1, 2025
db15134
reduce setting-event-page
MarineGiroux Oct 1, 2025
0f99f18
reduce general-info-event
MarineGiroux Oct 1, 2025
e91f9ae
reduce general-info-event and delete .scss files
MarineGiroux Oct 1, 2025
d630f08
reduce speaker and session list with pagination-component
MarineGiroux Oct 3, 2025
2a69cf2
reduce speaker and session list
MarineGiroux Oct 6, 2025
8787763
envent name information default
MarineGiroux Oct 6, 2025
723b872
link speaker with session collection
MarineGiroux Oct 7, 2025
610095b
session-speaker standelone
MarineGiroux Oct 8, 2025
c96d5f9
clean code session with speaker in firestore
MarineGiroux Oct 8, 2025
53be302
create modal speaker and session
MarineGiroux Oct 9, 2025
a9c15a1
delete extend FormModalService
MarineGiroux Oct 9, 2025
a0b9a8c
create session valid
MarineGiroux Oct 10, 2025
0322f10
create speaker valid
MarineGiroux Oct 10, 2025
eb118a1
create session, and abstractText default view
MarineGiroux Oct 10, 2025
142cc07
proposition track in create session
MarineGiroux Oct 10, 2025
37b16e7
resolve abstractText default for create session
MarineGiroux Oct 13, 2025
15245a7
create services for and event information and create session
MarineGiroux Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
111 changes: 8 additions & 103 deletions back/src/main/java/com/speakerspace/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.speakerspace.controller;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseToken;
import com.speakerspace.config.CookieService;
import com.speakerspace.config.FirebaseTokenRequest;
Expand All @@ -11,40 +9,36 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.nio.file.AccessDeniedException;

@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);

private final UserService userService;
private final CookieService cookieService;
private final FirebaseAuth firebaseAuth;

@PostMapping("/login")
public ResponseEntity<UserDTO> login(@RequestBody FirebaseTokenRequest request, HttpServletResponse response) {
if (request.idToken() == null) {
throw new IllegalArgumentException("No token provided");
}

FirebaseToken decodedToken = verifyFirebaseToken(request.idToken());
FirebaseToken decodedToken = userService.verifyFirebaseToken(request.idToken());
String uid = decodedToken.getUid();

cookieService.setAuthCookie(response, request.idToken());

UserDTO existingUser = userService.getUserByUid(uid);

if (existingUser == null) {
existingUser = createNewUser(decodedToken);
existingUser = userService.createNewUser(decodedToken);
} else {
existingUser = updateExistingUserIfNeeded(existingUser, decodedToken);
existingUser = userService.updateExistingUserIfNeeded(existingUser, decodedToken);
}

return ResponseEntity.ok(existingUser);
Expand All @@ -58,7 +52,7 @@ public ResponseEntity<Void> logout(HttpServletResponse response) {

@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody UserDTO userDTO) {
logger.info("Creating/updating user: {}", userDTO.uid());
log.info("Creating/updating user: {}", userDTO.uid());
UserDTO savedUser = userService.saveUser(userDTO);
return ResponseEntity.ok(savedUser);
}
Expand All @@ -74,7 +68,7 @@ public ResponseEntity<UserDTO> getUserByUid(@PathVariable String uid) {

@GetMapping("/user/{uid}")
public ResponseEntity<UserDTO> getUserData(@PathVariable String uid, HttpServletRequest request) {
authenticateAndAuthorize(request, uid);
userService.authenticateAndAuthorize(request, uid);

UserDTO userDTO = userService.getUserByUid(uid);
if (userDTO == null) {
Expand All @@ -86,7 +80,7 @@ public ResponseEntity<UserDTO> getUserData(@PathVariable String uid, HttpServlet

@PutMapping("/profile")
public ResponseEntity<UserDTO> updateUserProfile(@RequestBody UserDTO userDTO, HttpServletRequest request) {
String uid = authenticateAndAuthorize(request, userDTO.uid());
String uid = userService.authenticateAndAuthorize(request, userDTO.uid());

UserDTO existingUser = userService.getUserByUid(uid);
if (existingUser == null) {
Expand All @@ -96,93 +90,4 @@ public ResponseEntity<UserDTO> updateUserProfile(@RequestBody UserDTO userDTO, H
UserDTO updatedUser = userService.partialUpdateUser(userDTO, existingUser);
return ResponseEntity.ok(updatedUser);
}

private FirebaseToken verifyFirebaseToken(String idToken) {
try {
return firebaseAuth.verifyIdToken(idToken);
} catch (FirebaseAuthException e) {
logger.error("Firebase token verification failed: {}", e.getMessage());
throw new FirebaseAuthenticationException("Invalid token");
}
}

private UserDTO createNewUser(FirebaseToken decodedToken) {
UserDTO userDTO = UserDTO.builder()
.uid(decodedToken.getUid())
.email(decodedToken.getEmail())
.displayName(decodedToken.getName())
.photoURL(decodedToken.getPicture())
.build();

UserDTO createdUser = userService.saveUser(userDTO);
if (createdUser == null) {
throw new RuntimeException("Failed to create user");
}
return createdUser;
}

private UserDTO updateExistingUserIfNeeded(UserDTO existingUser, FirebaseToken decodedToken) {
boolean needsUpdate = false;
UserDTO.UserDTOBuilder builder = UserDTO.builder()
.uid(existingUser.uid())
.email(existingUser.email())
.displayName(existingUser.displayName())
.photoURL(existingUser.photoURL())
.company(existingUser.company())
.city(existingUser.city())
.phoneNumber(existingUser.phoneNumber())
.githubLink(existingUser.githubLink())
.twitterLink(existingUser.twitterLink())
.blueSkyLink(existingUser.blueSkyLink())
.linkedInLink(existingUser.linkedInLink())
.biography(existingUser.biography())
.otherLink(existingUser.otherLink());

if (existingUser.email() == null && decodedToken.getEmail() != null) {
builder.email(decodedToken.getEmail());
needsUpdate = true;
}

if ((existingUser.displayName() == null || existingUser.displayName().isEmpty())
&& decodedToken.getName() != null) {
builder.displayName(decodedToken.getName());
needsUpdate = true;
}

if ((existingUser.photoURL() == null || existingUser.photoURL().isEmpty())
&& decodedToken.getPicture() != null) {
builder.photoURL(decodedToken.getPicture());
needsUpdate = true;
}

if (needsUpdate) {
UserDTO updatedUserDTO = builder.build();
return userService.saveUser(updatedUserDTO);
}

return existingUser;
}

private String authenticateAndAuthorize(HttpServletRequest request, String targetUid) {
String token = cookieService.getAuthTokenFromCookies(request);
if (token == null) {
throw new UnauthorizedException("Authentication required");
}

try {
FirebaseToken decodedToken = firebaseAuth.verifyIdToken(token);
String tokenUid = decodedToken.getUid();

if (!tokenUid.equals(targetUid)) {
throw new AccessDeniedException("Not authorized to access this profile");
}

return tokenUid;
} catch (FirebaseAuthException | AccessDeniedException e) {
if (e.getMessage().contains("expired")) {
throw new TokenExpiredException("Token expired, please refresh");
}
throw new FirebaseAuthenticationException("Token verification failed", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.speakerspace.dto.EventDTO;
import com.speakerspace.exception.EntityNotFoundException;
import com.speakerspace.exception.UnauthorizedException;
import com.speakerspace.mapper.EventMapper;
import com.speakerspace.security.AuthenticationHelper;
import com.speakerspace.service.EventService;
import lombok.RequiredArgsConstructor;
Expand All @@ -21,34 +22,12 @@ public class EventController {

private final EventService eventService;
private final AuthenticationHelper authHelper;
private final EventMapper eventMapper;

@PostMapping("/create")
public ResponseEntity<EventDTO> createEvent(@RequestBody EventDTO eventDTO, Authentication authentication) {
if (authentication == null) {
throw new UnauthorizedException("Authentication required");
}

EventDTO eventWithUserId = EventDTO.builder()
.idEvent(eventDTO.idEvent())
.eventName(eventDTO.eventName())
.description(eventDTO.description())
.endDate(eventDTO.endDate())
.url(eventDTO.url())
.startDate(eventDTO.startDate())
.isOnline(eventDTO.isOnline())
.location(eventDTO.location())
.isPrivate(eventDTO.isPrivate())
.webLinkUrl(eventDTO.webLinkUrl())
.isFinish(eventDTO.isFinish())
.userCreateId(authHelper.getUserId(authentication))
.conferenceHallUrl(eventDTO.conferenceHallUrl())
.teamId(eventDTO.teamId())
.timeZone(eventDTO.timeZone())
.logoBase64(eventDTO.logoBase64())
.type(eventDTO.type())
.build();

EventDTO createdEvent = eventService.createEvent(eventWithUserId);
EventDTO mappedEvent = eventMapper.eventWithUserId(eventDTO, authentication);
EventDTO createdEvent = eventService.createEvent(mappedEvent);
return ResponseEntity.ok(createdEvent);
}

Expand Down Expand Up @@ -103,30 +82,12 @@ public ResponseEntity<EventDTO> updateEvent(
throw new AccessDeniedException("User not authorized to update this event");
}

EventDTO eventWithPreservedUserId = EventDTO.builder()
.idEvent(eventDTO.idEvent())
.eventName(eventDTO.eventName())
.description(eventDTO.description())
.endDate(eventDTO.endDate())
.url(eventDTO.url())
.startDate(eventDTO.startDate())
.isOnline(eventDTO.isOnline())
.location(eventDTO.location())
.isPrivate(eventDTO.isPrivate())
.webLinkUrl(eventDTO.webLinkUrl())
.isFinish(eventDTO.isFinish())
.userCreateId(existingEvent.userCreateId())
.conferenceHallUrl(eventDTO.conferenceHallUrl())
.teamId(eventDTO.teamId())
.timeZone(eventDTO.timeZone())
.logoBase64(eventDTO.logoBase64())
.type(eventDTO.type())
.build();

EventDTO updatedEvent = eventService.updateEvent(eventWithPreservedUserId);
EventDTO mergedEvent = eventMapper.mergeForUpdate(eventDTO, existingEvent, authentication);
EventDTO updatedEvent = eventService.updateEvent(mergedEvent);
return ResponseEntity.ok(updatedEvent);
}


@DeleteMapping("/{eventId}")
public ResponseEntity<Map<String, Object>> deleteEvent(@PathVariable String eventId) throws AccessDeniedException {
boolean deleted = eventService.deleteEvent(eventId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
import com.speakerspace.exception.EntityNotFoundException;
import com.speakerspace.exception.EventAuthorizationHelper;
import com.speakerspace.model.session.Session;
import com.speakerspace.model.session.SessionReviewImportData;
import com.speakerspace.model.session.SessionImportData;
import com.speakerspace.model.session.Speaker;
import com.speakerspace.service.SessionService;
import com.speakerspace.service.SpeakerService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -23,6 +24,7 @@ public class SessionController {

private final SessionService sessionService;
private final EventAuthorizationHelper authorizationHelper;
private final SpeakerService speakerService;

@PostMapping("/event/{eventId}/import")
public ResponseEntity<ImportResultDTO> importSessionsReview(
Expand All @@ -31,7 +33,7 @@ public ResponseEntity<ImportResultDTO> importSessionsReview(
Authentication authentication) throws AccessDeniedException {

return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () -> {
validateEventIdMatch(eventId, importRequest.eventId());
sessionService.validateEventIdMatch(eventId, importRequest.eventId());
return sessionService.importSessionsReview(eventId, importRequest.sessions());
});
}
Expand All @@ -43,21 +45,34 @@ public ResponseEntity<ImportResultDTO> importSessionsSchedule(
Authentication authentication) throws AccessDeniedException {

return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () -> {
validateEventIdMatch(eventId, importRequest.eventId());
validateSessionsData(importRequest.sessions());
sessionService.validateEventIdMatch(eventId, importRequest.eventId());
sessionService.validateSessionsData(importRequest.sessions());
return sessionService.importSessionsSchedule(eventId, importRequest.sessions());
});
}

@PostMapping("/event/{eventId}")
public ResponseEntity<SessionDTO> createSession(
@PathVariable String eventId,
@RequestBody
SessionCreateRequestDTO createRequest,
Authentication authentication) throws AccessDeniedException {

return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () -> {
sessionService.validateCreateRequest(createRequest);
return sessionService.createSession(eventId, createRequest);
});
}

@GetMapping("/event/{eventId}")
public ResponseEntity<List<SessionReviewImportData>> getSessionsByEventId(
public ResponseEntity<List<SessionImportData>> getSessionsByEventId(
@PathVariable String eventId,
HttpServletRequest request,
Authentication authentication) {

return authorizationHelper.executeWithUserAuthentication(request, authentication, () -> {
List<SessionReviewImportData> sessions = sessionService.getSessionsReviewAsImportData(eventId);
List<SessionReviewImportData> mutableSessions = new ArrayList<>(sessions);
List<SessionImportData> sessions = sessionService.getSessionsReviewAsImportData(eventId);
List<SessionImportData> mutableSessions = new ArrayList<>(sessions);
mutableSessions.sort(Comparator.comparing(s ->
s.getTitle() != null ? s.getTitle().toLowerCase() : ""
));
Expand All @@ -66,7 +81,7 @@ public ResponseEntity<List<SessionReviewImportData>> getSessionsByEventId(
}

@GetMapping("/event/{eventId}/session/{sessionId}/review")
public ResponseEntity<SessionReviewImportData> getSessionReviewById(
public ResponseEntity<SessionImportData> getSessionReviewById(
@PathVariable String eventId,
@PathVariable String sessionId,
HttpServletRequest request,
Expand All @@ -77,13 +92,19 @@ public ResponseEntity<SessionReviewImportData> getSessionReviewById(
}

@GetMapping("/event/{eventId}/session/{sessionId}")
public ResponseEntity<SessionDTO> getSessionDetailById(
public ResponseEntity<SessionImportData> getSessionDetailById(
@PathVariable String eventId,
@PathVariable String sessionId,
Authentication authentication) throws AccessDeniedException {
HttpServletRequest request,
Authentication authentication) {

return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () ->
sessionService.getSessionByIdAndEventId(sessionId, eventId));
return authorizationHelper.executeWithUserAuthentication(request, authentication, () -> {
SessionImportData session = sessionService.getSessionById(eventId, sessionId);
if (session == null) {
throw new EntityNotFoundException("Session not found with id: " + sessionId);
}
return session;
});
}

@GetMapping("/event/{eventId}/speakers")
Expand Down Expand Up @@ -122,8 +143,10 @@ public ResponseEntity<List<String>> getAvailableTracksForEvent(
@PathVariable String eventId,
Authentication authentication) throws AccessDeniedException {

return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () ->
sessionService.getDistinctTracksByEventId(eventId));
authorizationHelper.validateEventAuthorization(eventId, authentication);
List<String> tracks = sessionService.getAvailableTracksForEvent(eventId);

return ResponseEntity.ok(tracks);
}

@GetMapping("/event/{eventId}/calendar")
Expand Down Expand Up @@ -166,15 +189,14 @@ public ResponseEntity<Void> deleteSession(@PathVariable String id) {
return ResponseEntity.noContent().build();
}

private void validateEventIdMatch(String pathEventId, String bodyEventId) {
if (!pathEventId.equals(bodyEventId)) {
throw new IllegalArgumentException("Event ID mismatch");
}
}
@GetMapping("/event/{eventId}/empty-sessions")
public ResponseEntity<List<SessionDTO>> getEmptySessionsForEvent(
@PathVariable String eventId,
Authentication authentication) throws AccessDeniedException {

private void validateSessionsData(List<SessionScheduleImportDataDTO> sessions) {
if (sessions == null || sessions.isEmpty()) {
throw new IllegalArgumentException("No sessions data provided");
}
return authorizationHelper.executeWithEventAuthorization(eventId, authentication, () -> {
List<SessionDTO> emptySessions = speakerService.getEmptySessionsForEvent(eventId);
return emptySessions;
});
}
}
Loading