Use UserRole enum for session role handling
Replace string-based role handling with a typed UserRole enum. Adds src/entities/enums/user_role.dart and exports it from krow_domain. Update SessionHandlerMixin to use List<UserRole> and change fetchUserRole to return UserRole?. V2SessionService now derives the role via UserRole.fromSessionData, and client/staff SessionListener widgets pass const <UserRole>[...] when initializing the auth listener. This centralizes role derivation and eliminates scattered role string literals.
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart' show UserRole;
|
||||||
|
|
||||||
/// A widget that listens to session state changes and handles global reactions.
|
/// A widget that listens to session state changes and handles global reactions.
|
||||||
///
|
///
|
||||||
@@ -37,7 +38,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
final V2SessionService sessionService = Modular.get<V2SessionService>();
|
final V2SessionService sessionService = Modular.get<V2SessionService>();
|
||||||
|
|
||||||
sessionService.initializeAuthListener(
|
sessionService.initializeAuthListener(
|
||||||
allowedRoles: const <String>['CLIENT', 'BUSINESS', 'BOTH'],
|
allowedRoles: const <UserRole>[UserRole.business, UserRole.both],
|
||||||
);
|
);
|
||||||
|
|
||||||
_sessionSubscription = sessionService.onSessionStateChanged
|
_sessionSubscription = sessionService.onSessionStateChanged
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import 'package:krow_core/core.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart' show UserRole;
|
||||||
|
|
||||||
/// A widget that listens to session state changes and handles global reactions.
|
/// A widget that listens to session state changes and handles global reactions.
|
||||||
///
|
///
|
||||||
@@ -37,7 +38,7 @@ class _SessionListenerState extends State<SessionListener> {
|
|||||||
final V2SessionService sessionService = Modular.get<V2SessionService>();
|
final V2SessionService sessionService = Modular.get<V2SessionService>();
|
||||||
|
|
||||||
sessionService.initializeAuthListener(
|
sessionService.initializeAuthListener(
|
||||||
allowedRoles: const <String>['STAFF', 'BOTH'],
|
allowedRoles: const <UserRole>[UserRole.staff, UserRole.both],
|
||||||
);
|
);
|
||||||
|
|
||||||
_sessionSubscription = sessionService.onSessionStateChanged
|
_sessionSubscription = sessionService.onSessionStateChanged
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
|
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:krow_domain/krow_domain.dart' show UserRole;
|
||||||
|
|
||||||
/// Enum representing the current session state.
|
/// Enum representing the current session state.
|
||||||
enum SessionStateType { loading, authenticated, unauthenticated, error }
|
enum SessionStateType { loading, authenticated, unauthenticated, error }
|
||||||
@@ -85,11 +86,11 @@ mixin SessionHandlerMixin {
|
|||||||
firebase_auth.FirebaseAuth get auth;
|
firebase_auth.FirebaseAuth get auth;
|
||||||
|
|
||||||
/// List of allowed roles for this app (set during initialization).
|
/// List of allowed roles for this app (set during initialization).
|
||||||
List<String> _allowedRoles = <String>[];
|
List<UserRole> _allowedRoles = <UserRole>[];
|
||||||
|
|
||||||
/// Initialize the auth state listener (call once on app startup).
|
/// Initialize the auth state listener (call once on app startup).
|
||||||
void initializeAuthListener({
|
void initializeAuthListener({
|
||||||
List<String> allowedRoles = const <String>[],
|
List<UserRole> allowedRoles = const <UserRole>[],
|
||||||
}) {
|
}) {
|
||||||
_allowedRoles = allowedRoles;
|
_allowedRoles = allowedRoles;
|
||||||
|
|
||||||
@@ -112,10 +113,10 @@ mixin SessionHandlerMixin {
|
|||||||
/// Validates if user has one of the allowed roles.
|
/// Validates if user has one of the allowed roles.
|
||||||
Future<bool> validateUserRole(
|
Future<bool> validateUserRole(
|
||||||
String userId,
|
String userId,
|
||||||
List<String> allowedRoles,
|
List<UserRole> allowedRoles,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final String? userRole = await fetchUserRole(userId);
|
final UserRole? userRole = await fetchUserRole(userId);
|
||||||
return userRole != null && allowedRoles.contains(userRole);
|
return userRole != null && allowedRoles.contains(userRole);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Failed to validate user role: $e');
|
debugPrint('Failed to validate user role: $e');
|
||||||
@@ -123,11 +124,9 @@ mixin SessionHandlerMixin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches user role from the backend.
|
/// Fetches the user role from the backend by calling `GET /auth/session`
|
||||||
///
|
/// and deriving the [UserRole] from the response context.
|
||||||
/// Implementors should call `GET /auth/session` via [ApiService] and
|
Future<UserRole?> fetchUserRole(String userId);
|
||||||
/// extract the role from the response.
|
|
||||||
Future<String?> fetchUserRole(String userId);
|
|
||||||
|
|
||||||
/// Handle user sign-in event.
|
/// Handle user sign-in event.
|
||||||
Future<void> _handleSignIn(firebase_auth.User user) async {
|
Future<void> _handleSignIn(firebase_auth.User user) async {
|
||||||
@@ -135,7 +134,7 @@ mixin SessionHandlerMixin {
|
|||||||
_emitSessionState(SessionState.loading());
|
_emitSessionState(SessionState.loading());
|
||||||
|
|
||||||
if (_allowedRoles.isNotEmpty) {
|
if (_allowedRoles.isNotEmpty) {
|
||||||
final String? userRole = await fetchUserRole(user.uid);
|
final UserRole? userRole = await fetchUserRole(user.uid);
|
||||||
|
|
||||||
if (userRole == null) {
|
if (userRole == null) {
|
||||||
_emitSessionState(SessionState.unauthenticated());
|
_emitSessionState(SessionState.unauthenticated());
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ class V2SessionService with SessionHandlerMixin {
|
|||||||
|
|
||||||
/// Fetches the user role by calling `GET /auth/session`.
|
/// Fetches the user role by calling `GET /auth/session`.
|
||||||
///
|
///
|
||||||
/// Returns the role string (e.g. `STAFF`, `BUSINESS`, `BOTH`) or `null` if
|
/// Returns the [UserRole] derived from the session context, or `null` if
|
||||||
/// the call fails or the user has no role.
|
/// the call fails or the user has no role.
|
||||||
@override
|
@override
|
||||||
Future<String?> fetchUserRole(String userId) async {
|
Future<UserRole?> fetchUserRole(String userId) async {
|
||||||
try {
|
try {
|
||||||
final BaseApiService? api = _apiService;
|
final BaseApiService? api = _apiService;
|
||||||
if (api == null) {
|
if (api == null) {
|
||||||
@@ -61,16 +61,7 @@ class V2SessionService with SessionHandlerMixin {
|
|||||||
// Per V2 auth doc, GET /auth/session is used for app startup hydration.
|
// Per V2 auth doc, GET /auth/session is used for app startup hydration.
|
||||||
_hydrateSessionStores(data);
|
_hydrateSessionStores(data);
|
||||||
|
|
||||||
// Derive role from the presence of staff/business context.
|
return UserRole.fromSessionData(data);
|
||||||
// The session endpoint returns { user, tenant, business, vendor, staff }
|
|
||||||
// — there is no explicit "role" field.
|
|
||||||
final bool hasStaff = data['staff'] is Map<String, dynamic>;
|
|
||||||
final bool hasBusiness = data['business'] is Map<String, dynamic>;
|
|
||||||
|
|
||||||
if (hasStaff && hasBusiness) return 'BOTH';
|
|
||||||
if (hasStaff) return 'STAFF';
|
|
||||||
if (hasBusiness) return 'BUSINESS';
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export 'src/entities/enums/order_type.dart';
|
|||||||
export 'src/entities/enums/payment_status.dart';
|
export 'src/entities/enums/payment_status.dart';
|
||||||
export 'src/entities/enums/shift_status.dart';
|
export 'src/entities/enums/shift_status.dart';
|
||||||
export 'src/entities/enums/staff_status.dart';
|
export 'src/entities/enums/staff_status.dart';
|
||||||
|
export 'src/entities/enums/user_role.dart';
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
export 'src/core/services/api_services/api_response.dart';
|
export 'src/core/services/api_services/api_response.dart';
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/// The derived role of an authenticated user based on their session context.
|
||||||
|
///
|
||||||
|
/// Derived from the presence of `staff` and `business` keys in the
|
||||||
|
/// `GET /auth/session` response — the API does not return an explicit role.
|
||||||
|
enum UserRole {
|
||||||
|
/// User has a staff profile only.
|
||||||
|
staff,
|
||||||
|
|
||||||
|
/// User has a business membership only.
|
||||||
|
business,
|
||||||
|
|
||||||
|
/// User has both staff and business context.
|
||||||
|
both;
|
||||||
|
|
||||||
|
/// Derives the role from a session response map.
|
||||||
|
///
|
||||||
|
/// Returns `null` if neither `staff` nor `business` context is present.
|
||||||
|
static UserRole? fromSessionData(Map<String, dynamic> data) {
|
||||||
|
final bool hasStaff = data['staff'] is Map<String, dynamic>;
|
||||||
|
final bool hasBusiness = data['business'] is Map<String, dynamic>;
|
||||||
|
|
||||||
|
if (hasStaff && hasBusiness) return UserRole.both;
|
||||||
|
if (hasStaff) return UserRole.staff;
|
||||||
|
if (hasBusiness) return UserRole.business;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user