diff --git a/angular/RestClient/src/app/core/services/api/auth/auth-client.service.ts b/angular/RestClient/src/app/core/services/api/auth/auth-client.service.ts index b2b499bd6777e4279bea462a654c645225126a6e..acc7960912927cd746da7ef1ada81b89d50d1bab 100644 --- a/angular/RestClient/src/app/core/services/api/auth/auth-client.service.ts +++ b/angular/RestClient/src/app/core/services/api/auth/auth-client.service.ts @@ -11,36 +11,28 @@ export class AuthClientService { constructor(private http: HttpClient) {} login(email: String, password: String) { - return this.http.post( - `${this.URI}/login`, - { email, password } - // { - // headers: { - // 'Content-Type': 'application/json', - // 'Access-Control-Allow-Origin': '*', - // 'Access-Control-Allow-Methods': - // 'GET, POST, OPTIONS, PUT, PATCH, DELETE', - // 'Access-Control-Allow-Headers': 'X-Requested-With,content-type', - // 'Access-Control-Allow-Credentials': 'true', - // }, - // } - ); + return this.http.post(`${this.URI}/login`, { email, password }); } register(name: String, email: String, password: String, rol?: String) { - return this.http.post( - `${this.URI}/register`, - { - name, - email, - password, - rol, - } - // { - // headers: { - // 'Content-Type': 'application/json', - // }, - // } - ); + return this.http.post(`${this.URI}/register`, { + name, + email, + password, + rol, + }); + } + + changePassword(data: { + email: String; + password: String; + newPassword: String; + }) { + console.log(data); + return this.http.post(`${this.URI}/password`, data); + } + + deleteUser(userId: number, password: String) { + return this.http.post(`${this.URI}/delete/${userId}`, { password }); } } diff --git a/angular/RestClient/src/app/core/services/api/users/user-client.service.ts b/angular/RestClient/src/app/core/services/api/users/user-client.service.ts index a29e7de1e7378204281c29372e3695df836a33b2..16c2aab40eaa617429633a1a8f04da0bc53124f4 100644 --- a/angular/RestClient/src/app/core/services/api/users/user-client.service.ts +++ b/angular/RestClient/src/app/core/services/api/users/user-client.service.ts @@ -6,7 +6,6 @@ import { tap } from 'rxjs'; import { Client, ClientState, User } from '@features/users'; import { Session } from '@core/models'; - @Injectable({ providedIn: 'root', }) @@ -55,16 +54,4 @@ export class UserClientService { }) ); } - - // Cambiar la contraseña del usuario - updatePassword(currentPassword: string, newPassword: string) { - return this.http.patch( - `${this.URI}/me/password`, - { currentPassword, newPassword }, - { - observe: 'response', - responseType: 'text', - } - ); - } } diff --git a/angular/RestClient/src/app/features/bookings/booking/booking.component.ts b/angular/RestClient/src/app/features/bookings/booking/booking.component.ts index b6a95e812860501339d1155a06f1f42b7ec1983c..d39a3024544eb1ca72c1152ce2284672963e6f32 100644 --- a/angular/RestClient/src/app/features/bookings/booking/booking.component.ts +++ b/angular/RestClient/src/app/features/bookings/booking/booking.component.ts @@ -7,7 +7,7 @@ import { } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Booking} from '@features/bookings'; +import { Booking } from '@features/bookings'; import { User } from '@features/users'; import { LocalStorageService } from '../../../core/services/storage/local-storage.service'; import { BookingClientService } from '../../../core/services/api/bookings/booking-client.service'; @@ -18,8 +18,8 @@ type communication = { roomId: number; start: Date; end: Date; - hotelId : number; - managerId : number; + hotelId: number; + managerId: number; }; @Component({ @@ -32,12 +32,18 @@ type communication = { export class BookingComponent { user: User = { id: 0, email: '', name: '', rol: 'CLIENT' }; bookingForm: FormGroup; - bookingLocal: { roomId: number; start: Date; end: Date, hotelId : number, managerId : number } = { + bookingLocal: { + roomId: number; + start: Date; + end: Date; + hotelId: number; + managerId: number; + } = { roomId: 0, end: new Date(), start: new Date(), - hotelId : 0, - managerId : 0 + hotelId: 0, + managerId: 0, }; constructor( @@ -46,7 +52,6 @@ export class BookingComponent { private fb: FormBuilder, private sessionService: SessionService, private bookingClient: BookingClientService, - private userClient: UserClientService, private storage: LocalStorageService ) { // Inicialización del formulario con validaciones @@ -60,6 +65,8 @@ export class BookingComponent { this.router.navigate(['/booking', 'search']); return; } + console.log({ localBooking }); + this.bookingLocal = localBooking!; this.route.queryParams.subscribe((params) => { const roomId = Number(params['roomId']); @@ -101,10 +108,10 @@ export class BookingComponent { } submitBooking() { - const { id } = this.user; + const { id: userId } = this.user; const bookingRequest: any = { ...this.bookingLocal, - userId: id, + userId, }; // Llama al servicio para crear una nueva reserva @@ -113,8 +120,8 @@ export class BookingComponent { this.bookingClient.createBooking(bookingRequest).subscribe({ next: (response) => { console.log('Reserva creada con éxito', response); - this.storage.remove('booking-data'); - this.router.navigate(['/me', 'bookings']); + this.storage.remove('booking-data'); + this.router.navigate(['/me', 'bookings']); }, error: (error) => { console.error('Error al crear la reserva', error); diff --git a/angular/RestClient/src/app/features/bookings/user-booking-list/user-booking-list.component.ts b/angular/RestClient/src/app/features/bookings/user-booking-list/user-booking-list.component.ts index 120a94be3d6f9c55b32e60faa4c57d137ad78c90..222a16e9d12af7e395353b4bae25f12439e66153 100644 --- a/angular/RestClient/src/app/features/bookings/user-booking-list/user-booking-list.component.ts +++ b/angular/RestClient/src/app/features/bookings/user-booking-list/user-booking-list.component.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; -import { Booking} from '@features/bookings'; +import { Booking } from '@features/bookings'; import { User } from '@features/users'; import { ActivatedRoute, RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; @@ -86,66 +86,10 @@ export class UserBookingListComponent { this.bookingClient.deleteBooking(bookingId).subscribe({ next: () => { this.updateBookings(); - this.updateUserStatus(); }, error: (err) => { console.error('Error al eliminar una reserva', err); }, }); } - - updateUserStatus() { - this.bookingClient.getBookingsByUser(this.user.id).subscribe({ - next: (bookings) => { - const withActive = bookings.find( - (booking) => this.genBookingState(booking) === 'Reserva activa' - ); - const withInactive = bookings.find( - (booking) => this.genBookingState(booking) === 'Reserva inactiva' - ); - if (withActive) { - this.userClient - .alterUserStatus(this.user.id, 'WITH_ACTIVE_BOOKINGS') - .subscribe({ - next: (response) => { - console.log('Cambio de estado en el usuario a activo correcto'); - }, - error: (err) => { - console.error('Error al cambiar de estado al usuario a activo'); - }, - }); - } else if (withInactive) { - this.userClient - .alterUserStatus(this.user.id, 'WITH_INACTIVE_BOOKINGS') - .subscribe({ - next: (response) => { - console.log( - 'Cambio de estado en el usuario a inactivo correcto' - ); - }, - error: (err) => { - console.error( - 'Error al cambiar de estado al usuario a inactivo' - ); - }, - }); - } else { - this.userClient - .alterUserStatus(this.user.id, 'NO_BOOKINGS') - .subscribe({ - next: (response) => { - console.log( - 'Cambio de estado en el usuario a sin reservas correcto' - ); - }, - error: (err) => { - console.error( - 'Error al cambiar de estado al usuario sin reservas' - ); - }, - }); - } - }, - }); - } } diff --git a/angular/RestClient/src/app/features/hotels/hotel-list/hotel-list.component.ts b/angular/RestClient/src/app/features/hotels/hotel-list/hotel-list.component.ts index fd182a26860d562455a8c04ba7cb887ed4aa0e06..f9a21d8f1330adf8bc6968d6c44e18374b39828d 100644 --- a/angular/RestClient/src/app/features/hotels/hotel-list/hotel-list.component.ts +++ b/angular/RestClient/src/app/features/hotels/hotel-list/hotel-list.component.ts @@ -222,14 +222,16 @@ export class HotelListComponent { start: Date; end: Date; }; - const {managerId} = hotel; + console.log({ hotel }); + + const { managerId } = hotel; const hotelId = hotel.id; this.storage.save('booking-data', { roomId, start: start.toString(), end: end.toString(), hotelId, - managerId + managerId, }); this.router.navigate(['/me', 'bookings', 'new'], { queryParams: { roomId, startDate: start.toLocaleDateString() }, diff --git a/angular/RestClient/src/app/features/hotels/hotels.routes.ts b/angular/RestClient/src/app/features/hotels/hotels.routes.ts index bf57124cd5b570b3ea3329202bfbf95c5aa43fea..b5751fca22a35a3874b51a6e970ffb8a1220f661 100644 --- a/angular/RestClient/src/app/features/hotels/hotels.routes.ts +++ b/angular/RestClient/src/app/features/hotels/hotels.routes.ts @@ -12,7 +12,7 @@ export const HOTELS_ROUTES: AppRoute[] = [ path: 'register', // Registrar nuevo hotel component: HotelRegisterComponent, canActivate: [rolGuard], - data: { expectedRole: 'HOTEL_ADMIN' }, + data: { expectedRole: 'MANAGER' }, }, { path: ':id', // Hotel concreto diff --git a/angular/RestClient/src/app/features/users/main-page/main-page.component.ts b/angular/RestClient/src/app/features/users/main-page/main-page.component.ts index e8f1dd979264f7110216cdd3202031fabb39d79d..914e7511f9ab54403410d885e9bf27a1c1310808 100644 --- a/angular/RestClient/src/app/features/users/main-page/main-page.component.ts +++ b/angular/RestClient/src/app/features/users/main-page/main-page.component.ts @@ -38,6 +38,7 @@ export class MainPageComponent implements OnInit { this.userClient.getAllUsers().subscribe({ next: (data: Client[]) => { + console.log(data); this.users = data; this.filterUsers(); }, @@ -48,11 +49,18 @@ export class MainPageComponent implements OnInit { @ViewChild(MatPaginator) paginator?: MatPaginator; filterUsers(): void { - this.filteredUsers = + console.log( + this.selectedStatus, + this.users.map((u) => { + return { u, s: u?.status }; + }) + ); + + this.filteredUsers = this.selectedStatus === 'All' ? [...this.users] - : this.users.filter(user => user?.status === this.selectedStatus); - + : this.users.filter((user) => user?.status === this.selectedStatus); + this.dataSource = new MatTableDataSource<User>(this.filteredUsers); this.dataSource.paginator = this.paginator!; } diff --git a/angular/RestClient/src/app/features/users/types/User.interface.ts b/angular/RestClient/src/app/features/users/types/User.interface.ts index b3b6352295e6972050661d360ced3c2b0046a3da..27186ce576d239cf8d0e52b3c4928a8b1821e4f3 100644 --- a/angular/RestClient/src/app/features/users/types/User.interface.ts +++ b/angular/RestClient/src/app/features/users/types/User.interface.ts @@ -8,9 +8,8 @@ export interface User { rol: UserRol; } -export type UserRol = 'CLIENT' | 'HOTEL_ADMIN' | 'ADMIN'; -export const UserRolesArray: UserRol[] = ['CLIENT', 'HOTEL_ADMIN', 'ADMIN']; - +export type UserRol = 'CLIENT' | 'MANAGER' | 'ADMIN'; +export const UserRolesArray: UserRol[] = ['CLIENT', 'MANAGER', 'ADMIN']; export interface Client extends User { status: ClientState; @@ -28,4 +27,7 @@ export const ClientStateArray = [ 'WITH_ACTIVE_BOOKINGS', 'WITH_INACTIVE_BOOKINGS', ] as const; -export type ClientState = 'NO_BOOKINGS' | 'WITH_ACTIVE_BOOKINGS' | 'WITH_INACTIVE_BOOKINGS'; +export type ClientState = + | 'NO_BOOKINGS' + | 'WITH_ACTIVE_BOOKINGS' + | 'WITH_INACTIVE_BOOKINGS'; diff --git a/angular/RestClient/src/app/features/users/user-form/user-form.component.html b/angular/RestClient/src/app/features/users/user-form/user-form.component.html index 677115e1ef16e7735abdfe809b92fdb65262747e..0e32cc87fe42b7e2073f4ffa885c2dc912ba46c1 100644 --- a/angular/RestClient/src/app/features/users/user-form/user-form.component.html +++ b/angular/RestClient/src/app/features/users/user-form/user-form.component.html @@ -8,14 +8,18 @@ <div class="grid grid-flow-col mb-5"> @if (!isEditing) { <div> - @if (isHotelManager) { + @if (isManager) { <a [routerLink]="[getHotelsUri()]"> <button class="btn btn-primary">Mis hoteles</button> </a> - }@else if(!isAdmin){ + }@else if (isClient){ <a [routerLink]="[getBookingsUri()]"> <button class="btn btn-primary">Mis Reservas</button> </a> + }@else if (isAdmin) { + <a [routerLink]="['/admin', 'users']"> + <button class="btn btn-primary">Usuarios</button> + </a> } </div> } @@ -66,7 +70,8 @@ placeholder="Introduce tu email" /> </div> - } @if ((isChangePassword || isAuth) && (isMeRoute || isAuth)) { + } @if (((isChangePassword && !isAdmin) || isAuth) && (isMeRoute || + isAuth)) { <!-- Campo Contraseña Actual (solo en edición) --> <div class="form-group"> <label for="currentPassword">{{ currentPasswordText }}:</label> @@ -132,7 +137,6 @@ } <!-- Grupo de Botones --> <mat-card-actions class="flex justify-center mb-5"> - <!-- [disabled]="registerForm.invalid" --> <button type="submit" class="btn btn-success text-4xl" @@ -141,6 +145,12 @@ {{ submitButtonText }} </button> </mat-card-actions> + } @else if (!isEditing) { + <mat-card-actions class="flex justify-center mb-5"> + <button type="submit" class="btn btn-danger text-4xl"> + DELETE USER + </button> + </mat-card-actions> } </form> </mat-card-content> diff --git a/angular/RestClient/src/app/features/users/user-form/user-form.component.ts b/angular/RestClient/src/app/features/users/user-form/user-form.component.ts index e3b6e0965253a6241235415779abbfc4201263bc..055e9da2a122f6d8d9e1c54452572ac305454dca 100644 --- a/angular/RestClient/src/app/features/users/user-form/user-form.component.ts +++ b/angular/RestClient/src/app/features/users/user-form/user-form.component.ts @@ -14,7 +14,7 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; import { SessionService } from '../../../core/services/session/session.service'; import { Session } from '@core/models'; -import { UserRol, UserRolesArray} from '@features/users'; +import { UserRol, UserRolesArray } from '@features/users'; import { MatSelectModule } from '@angular/material/select'; import { Observable } from 'rxjs'; import { @@ -22,6 +22,7 @@ import { UserFormRoute, } from 'app/features/users/types/UserFormData'; import { getBasePath } from '@utils/utils'; +import { AuthClientService } from '@core/services'; const defaultUser: Session = { id: 0, @@ -70,12 +71,14 @@ export class UserFormComponent implements OnInit { currentPasswordText = 'Contraseña actual'; user = defaultUser; - isHotelManager = false; + isClient = false; + isManager = false; isAdmin = false; constructor( private fb: FormBuilder, private sessionService: SessionService, + private authService: AuthClientService, private userService: UserClientService, private route: ActivatedRoute, private router: Router @@ -142,11 +145,11 @@ export class UserFormComponent implements OnInit { setUp() { const snapshot = this.route.snapshot; - const urlSeg = snapshot.url; const { data } = snapshot as UserFormRoute; const mode = data!.mode!; console.log(mode); + this.isAdmin = !!mode.admin && mode.admin === true; switch (mode.formMode) { case 'REGISTER': @@ -183,7 +186,6 @@ export class UserFormComponent implements OnInit { default: break; } - this.isAdmin = !!mode.admin; this.mode = mode.formMode; this.initializeForm(); @@ -241,16 +243,20 @@ export class UserFormComponent implements OnInit { } private loadUser() { - // this.setData(); this.resolve().subscribe({ next: (user) => { this.user = user; - this.isHotelManager = (user.rol as UserRol) === 'HOTEL_ADMIN'; - this.isAdmin = (user.rol as UserRol) === 'ADMIN'; + this.isManager = (user.rol as UserRol) === 'MANAGER'; + this.isClient = (user.rol as UserRol) === 'CLIENT'; + this.isAdmin = this.isAdmin || (user.rol as UserRol) === 'ADMIN'; this.setData(); }, error: (error) => { console.error('Error:', error); + if (error.status == 404) + this.router.navigateByUrl( + getBasePath(this.router.url.split('/').slice(0, -1).join('/')) + ); }, }); } @@ -309,6 +315,15 @@ export class UserFormComponent implements OnInit { case 'PASSWORD': this.updatePassword(data.currentPassword, data.newPassword); break; + case 'VIEW': + const password = this.isAdmin + ? confirm('Desea eliminar el usuario') + ? 'password' + : undefined + : prompt('Confirma tu contraseña actual'); + if (!!password && password.trim().length != 0) + this.deleteUser(this.user.id, password); + break; default: break; } @@ -356,7 +371,32 @@ export class UserFormComponent implements OnInit { }); } - private updatePassword(password: string | undefined, newPassword: string) { - alert('Unimplemented yet'); + private updatePassword(password: string, newPassword: string) { + if (this.isAdmin) password = ''; + this.authService + .changePassword({ email: this.user.email, password, newPassword }) + .subscribe({ + next: () => { + alert('PASSWORD CHANGE!'); + this.router.navigateByUrl(getBasePath(this.router.url)); + }, + error: (error) => { + console.error(error); + // this.toastr.error('Invalid email or password'); + }, + }); + } + + private deleteUser(userId: number, password: string) { + this.authService.deleteUser(userId, password).subscribe({ + next: () => { + if (this.isAdmin) this.router.navigate(['/admin', 'users']); + else this.sessionService.logout(); + }, + error: (error) => { + console.error(error); + // this.toastr.error('Invalid email or password'); + }, + }); } } diff --git a/angular/RestClient/src/app/features/users/users.routes.ts b/angular/RestClient/src/app/features/users/users.routes.ts index aca2079caf16f9eb0010ac50ac872673ab659781..b22713021db10c382d3f89a37999fc8975d1631a 100644 --- a/angular/RestClient/src/app/features/users/users.routes.ts +++ b/angular/RestClient/src/app/features/users/users.routes.ts @@ -33,7 +33,7 @@ export const USERS_ROUTES: UserFormRoute[] = [ { // Usuario administrador de hoteles path: 'hotels', - data: { expectedRole: 'HOTEL_ADMIN' }, + data: { expectedRole: 'MANAGER' }, loadChildren: () => import('app/features/hotels').then((m) => m.MANAGERS_ROUTES), }, diff --git a/angular/RestClient/src/app/shared/navigation/navigation.component.ts b/angular/RestClient/src/app/shared/navigation/navigation.component.ts index a141bbaa99ec3c3003f08516ead69e39e0932822..43a9f88c852a42a154c11d8e9f1f350841e7aa6c 100644 --- a/angular/RestClient/src/app/shared/navigation/navigation.component.ts +++ b/angular/RestClient/src/app/shared/navigation/navigation.component.ts @@ -86,14 +86,14 @@ export class NavigationComponent implements OnInit { id: genId(), icon: 'hotel', text: 'Hoteles', - allowRoles: ['HOTEL_ADMIN'], + allowRoles: ['MANAGER'], link: '/me/hotels', }, { id: genId(), icon: 'fiber_new', text: 'Registrar hotel', - allowRoles: ['HOTEL_ADMIN'], + allowRoles: ['MANAGER'], link: '/hotels/register', }, { diff --git a/angular/RestClient/src/app/utils/utils.ts b/angular/RestClient/src/app/utils/utils.ts index a32f785ba732d2701656497706f176319d000cbc..e5ec01597501569bb46070264fa1063acf6a16e9 100644 --- a/angular/RestClient/src/app/utils/utils.ts +++ b/angular/RestClient/src/app/utils/utils.ts @@ -1,6 +1,6 @@ export function getBasePath(route: string) { const url = route.split('/').slice(1); - const me = '/me'; + const me = 'me'; let base = me; if (url.length > 0 && url[0] === me) { base = url[0]; @@ -15,6 +15,5 @@ export function getBasePath(route: string) { : me; } - console.log({ url, route, base }); - return base; + return `/${base}`; } diff --git a/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java b/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java index da67a0353174a2d2fe816a5925a378a91702d8df..05c47753414bb639e114741d4eb458b0026c7874 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/api/UserAPI.java @@ -82,10 +82,10 @@ public class UserAPI { restTemplate.put(url, body, id); } - public void deleteUser(User user) { + public void deleteUser(int id) { String url = USER_API_URL + "/{id}"; - restTemplate.delete(url, user.getId()); + restTemplate.delete(url, id); } } diff --git a/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java b/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java index f84a840e5cf18b51da4e5f454e9d9d8433a0d5f8..1a40686ce6c559392b05d6365d0c770b974d37bf 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/controllers/AuthController.java @@ -40,13 +40,14 @@ public class AuthController { String token = authorization.substring(7); + String email = json.get("email"); String actualPassword = json.get("password"); String newPassword = json.get("newPassword"); - return authService.changePassword(token, actualPassword, newPassword); + return authService.changePassword(token, email, actualPassword, newPassword); } - @DeleteMapping("/delete/{id}") + @PostMapping("/delete/{id}") public Object postMethodName(@PathVariable int id, @RequestBody Map<String, String> json, @RequestHeader(value = "Authorization", required = true) String authorization) { if (authorization == null || !authorization.startsWith("Bearer ")) diff --git a/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java b/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java index f82af24455cff5009132f7f8a16f7cd36d279f4a..40ab68cf12ebbdabc2f8abf526907641ac458034 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/exceptions/GlobalExceptionHandler.java @@ -18,6 +18,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, ex.getStatusCode()); } @@ -27,6 +29,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", "An unexpected error occurred: " + ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java b/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java index 5cf22ae7ab1421418aa9feebd7b22fe9728f696d..da9b897e4651e4da793d410683beb2831b608160 100644 --- a/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java +++ b/java/services/auth/src/main/java/com/uva/api/auth/services/AuthService.java @@ -71,22 +71,25 @@ public class AuthService { } private User getUser(String email, String password) { + return getUser(email, password, false); + } + + private User getUser(String email, String password, boolean isAdmin) { User user = userAPI.getUserByEmail(email); - boolean correctPassword = SecurityUtils.checkPassword(password, user.getPassword()); + boolean correctPassword = isAdmin || SecurityUtils.checkPassword(password, user.getPassword()); return correctPassword ? user : null; } - public ResponseEntity<?> changePassword(String token, String actualPass, String newPass) { + public ResponseEntity<?> changePassword(String token, String email, String actualPass, String newPass) { JwtData decoded = jwtUtil.decodeToken(token); if (decoded == null) throw new HttpClientErrorException(HttpStatus.FORBIDDEN); - String email = decoded.getEmail(); - User user = getUser(email, actualPass); + User user = getUser(email, actualPass, decoded.isAdmin()); - boolean changePasswordAllowed = decoded.isAdmin() || user != null; + boolean changePasswordAllowed = decoded.isAdmin() || (user != null && validStrings(actualPass)); - if (user != null && !validStrings(actualPass, newPass)) + if (user != null && !validStrings(newPass)) throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); if (!changePasswordAllowed) @@ -104,19 +107,23 @@ public class AuthService { if (decoded == null) throw new HttpClientErrorException(HttpStatus.FORBIDDEN); - String email = decoded.getEmail(); - User user = getUser(email, password); + boolean deleteAllowed = decoded.isAdmin(); + if (!deleteAllowed) { // no admin + String email = decoded.getEmail(); - boolean changePasswordAllowed = decoded.isAdmin() - || (user != null && user.getId() == id); + User user = getUser(email, password); - if (user != null && !validStrings(password)) - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); + if (user == null || !validStrings(password)) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST); - if (!changePasswordAllowed) + // Verificamos si es el dueño del recurso + deleteAllowed = user.getId() == id; + } + + if (!deleteAllowed) throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "Invalid credentials"); - userAPI.deleteUser(user); - return ResponseEntity.ok(user); + userAPI.deleteUser(id); + return new ResponseEntity<>(HttpStatus.ACCEPTED); } } diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/exceptions/GlobalExceptionHandler.java b/java/services/bookings/src/main/java/com/uva/api/bookings/exceptions/GlobalExceptionHandler.java index b53333d1ce602da5343654e58643eb5278d272b1..235160805c5ff7907c1af82b56472d2ef4e5d3b5 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/exceptions/GlobalExceptionHandler.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/exceptions/GlobalExceptionHandler.java @@ -28,6 +28,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, ex.getStatusCode()); } @@ -47,6 +49,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", "An unexpected error occurred: " + ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java b/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java index 3ebed8a9abf0def8f1d8a78789b13419f7ec860a..7b4176bbdd5b4a86289eba5fdecb7d0266245829 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java @@ -186,10 +186,11 @@ public class BookingService { public ResponseEntity<?> deleteBooking(String token, Integer id) { Booking booking = findById(token, id); bookingRepository.deleteById(id); + bookingRepository.flush(); - ClientStatus status = calculateClientStatus(id); + ClientStatus status = calculateClientStatus(booking.getUserId()); // In this case, the check if the client has already de state is expensive - userApi.updateClientState(id, status); + userApi.updateClientState(booking.getUserId(), status); return ResponseEntity.ok(booking); } @@ -198,9 +199,10 @@ public class BookingService { Function<Integer, List<Booking>> findAction, Consumer<Integer> deleteAction) { List<Booking> bookings = findAction.apply(id); - if (bookings.isEmpty()) { + + if (bookings.isEmpty()) return new ArrayList<>(); - } + deleteAction.accept(id); return bookings; diff --git a/java/services/hotels/src/main/java/com/uva/api/hotels/config/SecurityConfig.java b/java/services/hotels/src/main/java/com/uva/api/hotels/config/SecurityConfig.java index 800461c93bb53192077ed9dc5d2b897ed644da14..c452bae044fe48bd4c79a5ee8f687f78d6a9ebd0 100644 --- a/java/services/hotels/src/main/java/com/uva/api/hotels/config/SecurityConfig.java +++ b/java/services/hotels/src/main/java/com/uva/api/hotels/config/SecurityConfig.java @@ -61,7 +61,7 @@ public class SecurityConfig { // Permitir OPTIONS sin autenticación .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() // Acceso restringido - .requestMatchers(GET, "/hotels*", "/hotels/**").authenticated() + .requestMatchers(GET, "/hotels*", "/hotels/**").permitAll() .requestMatchers(POST, "/hotels*") .hasAnyAuthority(flat(ADMIN, MANAGER)) diff --git a/java/services/hotels/src/main/java/com/uva/api/hotels/controllers/HotelController.java b/java/services/hotels/src/main/java/com/uva/api/hotels/controllers/HotelController.java index b60bf510d446f8cc2e91a18e13f009018235288d..9276ef66ad49836836ae54d3ff7c06e91c19a527 100644 --- a/java/services/hotels/src/main/java/com/uva/api/hotels/controllers/HotelController.java +++ b/java/services/hotels/src/main/java/com/uva/api/hotels/controllers/HotelController.java @@ -22,11 +22,11 @@ public class HotelController { @GetMapping public ResponseEntity<?> getAllHotels( - @RequestHeader(value = "Authorization", required = true) String authorization, + @RequestHeader(value = "Authorization", required = false) String authorization, @RequestParam(required = false) Integer managerId, @RequestParam(required = false) LocalDate start, @RequestParam(required = false) LocalDate end) { - String token = Utils.getToken(authorization); + String token = Utils.getTokenSoft(authorization); return hotelService.getAllHotels(token, managerId, start, end); } @@ -47,6 +47,7 @@ public class HotelController { public ResponseEntity<?> deleteHotelsByManagerId( @RequestHeader(value = "Authorization", required = true) String authorization, @RequestParam(required = true) Integer managerId) { + System.out.println(authorization); String token = Utils.getToken(authorization); return hotelService.deleteHotelsByManagerId(token, managerId); } diff --git a/java/services/hotels/src/main/java/com/uva/api/hotels/exceptions/GlobalExceptionHandler.java b/java/services/hotels/src/main/java/com/uva/api/hotels/exceptions/GlobalExceptionHandler.java index b9a7143f92e4951fa55681f4a76411a00a083d50..52175b34f0e2fdc388193871b1c224d209803a3b 100644 --- a/java/services/hotels/src/main/java/com/uva/api/hotels/exceptions/GlobalExceptionHandler.java +++ b/java/services/hotels/src/main/java/com/uva/api/hotels/exceptions/GlobalExceptionHandler.java @@ -19,6 +19,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } @@ -28,6 +30,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST); } @@ -38,6 +42,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, ex.getStatusCode()); } @@ -57,6 +63,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", "An unexpected error occurred: " + ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/java/services/hotels/src/main/java/com/uva/api/hotels/services/HotelService.java b/java/services/hotels/src/main/java/com/uva/api/hotels/services/HotelService.java index d06db4347e85a6ec95285c531a9d1540d325fa15..e84586a13ad5ebe27686ed8e94e94b96ced0bcca 100644 --- a/java/services/hotels/src/main/java/com/uva/api/hotels/services/HotelService.java +++ b/java/services/hotels/src/main/java/com/uva/api/hotels/services/HotelService.java @@ -16,7 +16,6 @@ import com.uva.api.hotels.exceptions.InvalidDateRangeException; import com.uva.api.hotels.exceptions.InvalidRequestException; import com.uva.api.hotels.models.Hotel; import com.uva.api.hotels.models.Room; -import com.uva.api.hotels.models.external.users.UserRol; import com.uva.api.hotels.repositories.HotelRepository; import com.uva.api.hotels.repositories.RoomRepository; @@ -58,11 +57,6 @@ public class HotelService { if (hotels.isEmpty()) throw new InvalidRequestException("No hotels"); - if (!tokenService.hasAnyRole(token, UserRol.ADMIN, UserRol.MANAGER)) - hotels = hotels.stream().map(h -> { - h.setManagerId(null); - return h; - }).toList(); return ResponseEntity.ok(hotels); } diff --git a/java/services/hotels/src/main/java/com/uva/api/hotels/utils/Utils.java b/java/services/hotels/src/main/java/com/uva/api/hotels/utils/Utils.java index 2a54d3f23cc4ca913b0cdd81c0cd94cb2cd5c5e6..1465d408253d7edea9cc378abffabb4784a6e227 100644 --- a/java/services/hotels/src/main/java/com/uva/api/hotels/utils/Utils.java +++ b/java/services/hotels/src/main/java/com/uva/api/hotels/utils/Utils.java @@ -4,10 +4,17 @@ import org.springframework.http.HttpStatus; import org.springframework.web.client.HttpClientErrorException; public class Utils { - public static String getToken(String authorization) { + public static String getTokenSoft(String authorization) { String prefix = "Bearer "; - if (!authorization.startsWith(prefix)) + return authorization != null && authorization.startsWith(prefix) + ? authorization.substring(prefix.length()) + : null; + } + + public static String getToken(String authorization) { + String token = getTokenSoft(authorization); + if (token == null) throw new HttpClientErrorException(HttpStatus.FORBIDDEN); - return authorization.substring(prefix.length()); + return token; } } diff --git a/java/services/users/src/main/java/com/uva/api/users/api/BookingAPI.java b/java/services/users/src/main/java/com/uva/api/users/api/BookingAPI.java index 60d0800b0b8581162bd4fbd509dc762dfd263dac..556c63afdffea51780aca38748425c173546194b 100644 --- a/java/services/users/src/main/java/com/uva/api/users/api/BookingAPI.java +++ b/java/services/users/src/main/java/com/uva/api/users/api/BookingAPI.java @@ -1,21 +1,29 @@ package com.uva.api.users.api; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; @Component public class BookingAPI { @Autowired + @Qualifier("IdentifyRestTemplate") private RestTemplate restTemplate; @Value("${services.external.bookings.url}") private String BOOKING_API_URL; public void deleteAllByUserId(int id) { - String url = BOOKING_API_URL + "?userId={id}"; - restTemplate.delete(url, id); + try { + String url = BOOKING_API_URL + "?userId={id}"; + restTemplate.delete(url, id); + } catch (HttpClientErrorException ex) { + if (!ex.getMessage().contains("404 No bookings for this hotel")) + throw ex; + } } } diff --git a/java/services/users/src/main/java/com/uva/api/users/api/HotelApi.java b/java/services/users/src/main/java/com/uva/api/users/api/HotelApi.java index c628922f05670c5830bfa1a2bfded9cfa70e3d1b..60803b4e672d937fa961bf743b4a228f4f640cec 100644 --- a/java/services/users/src/main/java/com/uva/api/users/api/HotelApi.java +++ b/java/services/users/src/main/java/com/uva/api/users/api/HotelApi.java @@ -1,6 +1,7 @@ package com.uva.api.users.api; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @@ -9,6 +10,7 @@ import org.springframework.web.client.RestTemplate; public class HotelApi { @Autowired + @Qualifier("IdentifyRestTemplate") private RestTemplate restTemplate; @Value("${services.external.hotels.url}") diff --git a/java/services/users/src/main/java/com/uva/api/users/config/RestTemplateConfig.java b/java/services/users/src/main/java/com/uva/api/users/config/RestTemplateConfig.java index 6b9efac7e5516734157e71d98ede467922bd3a83..2f005cfd725471b4b98d5d4f78c9ae708e8b7a91 100644 --- a/java/services/users/src/main/java/com/uva/api/users/config/RestTemplateConfig.java +++ b/java/services/users/src/main/java/com/uva/api/users/config/RestTemplateConfig.java @@ -14,7 +14,8 @@ public class RestTemplateConfig { return new RestTemplate(); } - public RestTemplate restTemplate(RestTemplateInterceptor interceptor) { + @Bean("IdentifyRestTemplate") + RestTemplate restTemplate(RestTemplateInterceptor interceptor) { RestTemplate restTemplate = new RestTemplate(); restTemplate.setInterceptors(List.of(interceptor)); return restTemplate; diff --git a/java/services/users/src/main/java/com/uva/api/users/config/SecurityConfig.java b/java/services/users/src/main/java/com/uva/api/users/config/SecurityConfig.java index c775e9fba594bf230b448595dcb266ff827b89c0..bcc1933f707e39b987e9eb935f5f7d2740d8d858 100644 --- a/java/services/users/src/main/java/com/uva/api/users/config/SecurityConfig.java +++ b/java/services/users/src/main/java/com/uva/api/users/config/SecurityConfig.java @@ -67,6 +67,9 @@ public class SecurityConfig { .requestMatchers(PATCH, "/users/clients/" + id) .hasAuthority(BOOKINGS.toString()) + .requestMatchers(DELETE, "/users/{id}") + .hasAnyAuthority(join(flat(ADMIN), flat(AUTHENTICATION))) + .requestMatchers("/users/clients/**") .hasAnyAuthority(anyService(ADMIN, CLIENT)) diff --git a/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java b/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java index f7e1e6feb8bb814a4e557171bff84e2889c89895..df1bd5ec394ab004f39bbaacd1e1cce50591bac0 100644 --- a/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java +++ b/java/services/users/src/main/java/com/uva/api/users/exceptions/GlobalExceptionHandler.java @@ -19,6 +19,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.NOT_FOUND); } @@ -28,6 +30,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, ex.getStatusCode()); } @@ -37,6 +41,8 @@ public class GlobalExceptionHandler { body.put("timestamp", LocalDateTime.now()); body.put("message", "An unexpected error occurred: " + ex.getMessage()); + ex.printStackTrace(System.err); + return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR); } } diff --git a/java/services/users/src/main/java/com/uva/api/users/services/TokenService.java b/java/services/users/src/main/java/com/uva/api/users/services/TokenService.java index 583e5733a22fb9cfdd31ca11f02e33e3d130fbd7..02707e46a97bf35cc1cc9e4b45c6761337d63b69 100644 --- a/java/services/users/src/main/java/com/uva/api/users/services/TokenService.java +++ b/java/services/users/src/main/java/com/uva/api/users/services/TokenService.java @@ -78,6 +78,6 @@ public class TokenService { boolean isAdmin = decoded.isAdmin(); boolean isService = decoded.getService() != null && decoded.getAudience().equals("INTERNAL"); if (!isAdmin && !isService) - throw new HttpClientErrorException(HttpStatus.FORBIDDEN); + throw new HttpClientErrorException(HttpStatus.FORBIDDEN, "XD"); } }