Skip to main content

Overview

Trippins implements a role-based access control (RBAC) system with three distinct user types: Anonymous users, Registered users (Clients), and Administrators. Each role has different permissions and access levels within the platform.

User Role Hierarchy

Anonymous

Public visitors (no account)

Registered User

Authenticated clients (ROLE_USER)

Administrator

Platform admins (ROLE_ADMIN)

User Entity Structure

Base User Entity

All users inherit from a common base entity:
@Entity
@Table(name = "User")
public class User {
    @Id
    @Column(name = "dni", nullable = false, unique = true, length = 9)
    private String dni;

    @Column(name = "name", nullable = false, unique = true, length = 100)
    private String name;

    @Column(name = "number", nullable = false, unique = true, length = 9)
    private Integer number;

    @Column(name = "password", nullable = false, length = 20)
    private String password;

    @Column(name = "email", nullable = false, unique = true, length = 40)
    private String email;

    @Column(name = "is_admin", nullable = false)
    protected Boolean admin;

    @ElementCollection(fetch = FetchType.EAGER)
    private List<String> roles;

    @Column(name = "encoded_password", nullable = true)
    private String encodedPassword;
}

Client Entity

Regular users are represented by the Client entity:
@Entity
public class Client extends User {

    public Client() {}
    
    public Client(String dni, String name, Integer number, String password, String email) {
        super(dni, name, number, password, email);
        this.admin = false;  // Not an admin
    }
}

Admin Entity

Administrators have elevated privileges:
@Entity
public class Admin extends User {

    public Admin() {}

    public Admin(String dni, String name, Integer number, String password, String email) {
        super(dni, name, number, password, email);
        this.admin = true;  // Is an admin
    }
}

Anonymous Users

Capabilities

Anonymous users (not logged in) can:

Browse Properties

View the housing catalog at /room

Search & Filter

Use search functionality with tags and stars

View Public Pages

Access home, about, contact pages

Register Account

Create a new user account

Restrictions

Anonymous users cannot:
  • View property details
  • Make reservations
  • Write reviews
  • Access user profile
  • Create new properties

Security Configuration

.authorizeHttpRequests(auth -> auth
    // Public endpoints for anonymous users
    .requestMatchers("/", "/index", "/about", "/contact", "/register").permitAll()
    .requestMatchers("/room", "/v1/api/query").permitAll()
    .requestMatchers(HttpMethod.POST, "/addUser", "/v1/api/users").permitAll()
    .requestMatchers(HttpMethod.POST, "/v1/api/login").permitAll()
    // ... authenticated routes require login
)
When an anonymous user tries to access a protected resource, they are redirected to the login page with a returnUrl parameter to redirect back after authentication.

Registered Users (Clients)

Role Assignment

When users register, they automatically receive the ROLE_USER role:
// During registration
this.userService.createAccount({
  name: formData.name,
  email: formData.email,
  dni: formData.dni,
  number: formData.number,
  password: formData.password
}).subscribe({
  next: () => {
    // User created with ROLE_USER
    this.router.navigate(['new/login']);
  }
});

User Capabilities

Registered users have all anonymous capabilities plus:
1

View Property Details

Access full property information at /room/{code}
2

Make Reservations

Book accommodations for specific dates
3

Write Reviews

Submit ratings and comments for properties
4

Manage Profile

View and update personal information
5

Create Properties

Submit new hotel listings (requires admin approval)

User DTO Structure

public class UserDTO {
    private String dni;
    private String name;
    private Integer number;
    private String email;
    private Boolean admin;       // false for regular users
    private List<String> roles;  // ["ROLE_USER"]
}

User Permissions in Security Config

.authorizeHttpRequests(auth -> auth
    // Authenticated users (ROLE_USER and ROLE_ADMIN)
    .requestMatchers("/room/{code}", "/roomDetails").authenticated()
    .requestMatchers("/newHotel", "/booking", "/profile").authenticated()
    .requestMatchers(HttpMethod.POST, "/addHotel").hasAnyRole("USER", "ADMIN")
    // ...
)

Checking User Authentication

// Check if user is logged in
isLoggedIn(): boolean {
  return !!this.getJwt();
}

// Get user roles
getRoles(): string[] {
  const rolesJson = localStorage.getItem(this.ROLES_KEY);
  return rolesJson ? JSON.parse(rolesJson) : [];
}

// Check specific role
hasRole(role: string): boolean {
  return this.getRoles().includes(role);
}

Administrators

Admin Capabilities

Administrators have all user capabilities plus exclusive admin functions:

User Management

View, create, update, and delete all users

Review Moderation

Manage all reviews across the platform

Property Approval

Approve or reject housing submissions

Reservation Management

View and manage all reservations

Admin-Only Endpoints

.authorizeHttpRequests(auth -> auth
    // Admin-only access
    .requestMatchers("/v1/api/users/**").hasRole("ADMIN")
    .requestMatchers("/v1/api/reviews/**").hasRole("ADMIN")
    .requestMatchers("/v1/api/admin/**").hasRole("ADMIN")
    .requestMatchers("/admin/**", "/admin").hasRole("ADMIN")
    // ...
)

Admin Panel Routes

// Angular routing with admin guard
{
  path: 'admin',
  component: AdminComponent,
  canActivate: [AdminGuard],  // Checks for ROLE_ADMIN
  children: [
    { path: 'housing', component: HousingPanelComponent },
    { path: 'reservations', component: ReservationPanelComponent },
    { path: 'users', component: UserPanelComponent },
    { path: 'reviews', component: ReviewPanelComponent }
  ]
}

Admin Role Verification

// Check if current user is admin
isAdmin(): boolean {
  return this.authService.hasRole('ROLE_ADMIN');
}

// Check for any required role
hasAnyRole(roles: string[]): boolean {
  var currentRoles: string[] = this.getRoles();
  for (let index = 0; index < roles.length; index++) {
    if (currentRoles.includes(roles[index])) {
      return true;
    }
  }
  return false;
}

Role-Based API Access

User Management APIs

GET /v1/api/users

Get all users (admin only)
@GetMapping
@SecurityRequirement(name = "JWT")
public ResponseEntity<List<UserDTO>> getAllUsers() {
    // Only accessible to ROLE_ADMIN
    List<UserDTO> users = userService.getAllUsers();
    return ResponseEntity.ok(users);
}

POST /v1/api/users

Create new user (public for registration)
@PostMapping
@SecurityRequirement(name = "JWT")
public ResponseEntity<UserDTO> createUser(@RequestBody RegisteredUserDTO user) {
    UserDTO createdUser = userService.createUser(user);
    return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}

PUT /v1/api/users/{id}

Update user (admin only)

DELETE /v1/api/users/{id}

Delete user (admin only)

Review Management APIs

Access: Any authenticated user (ROLE_USER or ROLE_ADMIN)Users can create reviews for properties they’ve visited.
Access: Admin only (ROLE_ADMIN)Retrieve complete list of all reviews across all properties.
Access: Admin only (ROLE_ADMIN)Modify any review (useful for moderation).
Access: Admin only (ROLE_ADMIN)Remove inappropriate or spam reviews.

Permission Matrix

ActionAnonymousRegistered UserAdministrator
View housing list
Search properties
View property details
Make reservation
Write review
Create property✅ (pending approval)
Approve property
View all users
Manage users
Moderate reviews
View all reservationsOwn only✅ All

Role Checking in Components

Frontend Role Guards

// Admin guard
@Injectable()
export class AdminGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.hasRole('ROLE_ADMIN')) {
      return true;
    }
    this.router.navigate(['/']);
    return false;
  }
}

// Auth guard (any logged-in user)
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    }
    
    // Redirect to login with return URL
    this.router.navigate(['/new/login'], {
      queryParams: { returnUrl: route.url }
    });
    return false;
  }
}

Conditional UI Elements

<div *ngIf="authService.isLoggedIn()">
  <!-- Shown to logged-in users -->
  <button (click)="makeReservation()">Book Now</button>
</div>

<div *ngIf="authService.hasRole('ROLE_ADMIN')">
  <!-- Shown only to admins -->
  <button (click)="approveProperty()">Approve</button>
  <button (click)="deleteUser()">Delete User</button>
</div>

<div *ngIf="!authService.isLoggedIn()">
  <!-- Shown to anonymous users -->
  <a routerLink="/new/login">Log in to book</a>
</div>

JWT Token and Roles

Token Structure

When users log in, they receive a JWT token containing their roles:
{
  "jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "roles": ["ROLE_USER"]  // or ["ROLE_ADMIN"]
}

Role Storage

// Store roles in localStorage
private storeRoles(roles: string[]): void {
  localStorage.setItem(this.ROLES_KEY, JSON.stringify(roles));
}

// Retrieve roles
getRoles(): string[] {
  const rolesJson = localStorage.getItem(this.ROLES_KEY);
  return rolesJson ? JSON.parse(rolesJson) : [];
}

Backend Role Extraction

@PostMapping("/v1/api/login")
public ResponseEntity<?> createAuthenticationToken(
    @RequestBody AuthenticationRequest authenticationRequest) {
    
    // Authenticate user
    authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(
            authenticationRequest.getEmail(),
            authenticationRequest.getPassword()
        )
    );

    // Load user details
    final UserDetails userDetails = userDetailsService
        .loadUserByUsername(authenticationRequest.getEmail());
    
    // Generate JWT
    final String jwt = jwtUtil.generateToken(userDetails);

    // Extract roles from authorities
    List<String> roles = userDetails.getAuthorities().stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList());

    return ResponseEntity.ok(new AuthenticationResponse(jwt, roles));
}

Best Practices

Grant users only the minimum permissions needed to perform their tasks. Don’t give admin rights unnecessarily.
Hide or disable UI elements that users don’t have permission to use:
<button *ngIf="canEditUser()" (click)="edit()">Edit</button>
Always validate permissions on the backend, never trust client-side role checks:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(String id) { ... }
Log all admin actions for security and compliance:
logger.info("Admin {} deleted user {}", adminId, userId);
Consider implementing role hierarchy where ADMIN inherits USER permissions automatically.

Promoting Users to Admin

Manual Database Update

Currently, users are promoted to admin via direct database modification:
-- Update user to admin
UPDATE User SET is_admin = true WHERE dni = '12345678A';

-- Add ROLE_ADMIN to user's roles
-- (Implementation depends on your ElementCollection storage)

Programmatic Promotion (Future Enhancement)

Consider implementing an API endpoint for admin promotion:
@PutMapping("/users/{id}/promote")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<UserDTO> promoteToAdmin(@PathVariable String id) {
    UserDTO user = userService.getUserById(id);
    user.setAdmin(true);
    user.getRoles().add("ROLE_ADMIN");
    UserDTO updated = userService.updateUser(id, user);
    return ResponseEntity.ok(updated);
}
Admin promotion should be strictly controlled and logged. Consider implementing approval workflows or requiring multiple admin approvals for sensitive operations.

Security Considerations

Trippins uses stateless JWT authentication:
.sessionManagement(session -> session
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
CSRF is disabled for stateless API:
.csrf(csrf -> csrf.disable())
Note: Re-enable CSRF if you add session-based authentication.
All passwords are encrypted with BCrypt:
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
Always use HTTPS in production to protect JWT tokens during transmission.

Troubleshooting

  • Verify user has ROLE_ADMIN in their roles array
  • Check JWT token contains correct roles
  • Ensure token is being sent in Authorization header
  • Verify Spring Security configuration allows admin access
  • Confirm user is logged in (isLoggedIn() returns true)
  • Check JWT token is stored in localStorage
  • Verify token hasn’t expired
  • Ensure route guard is properly configured
  • Check if storeRoles() is being called after login
  • Verify localStorage is not being cleared
  • Ensure roles are included in login response
  • Check browser’s localStorage in DevTools

Authentication

Learn how to log in and register

Making Reservations

Requires registered user role

Writing Reviews

Available to authenticated users

Browsing Housing

Public access with enhanced features for users

Build docs developers (and LLMs) love