Skip to content

Commit deb906e

Browse files
authored
Merge pull request #16 from headlines-toolkit/fix_authentication
Fix authentication
2 parents b7f1a22 + 9e89851 commit deb906e

File tree

5 files changed

+58
-13
lines changed

5 files changed

+58
-13
lines changed

lib/src/config/app_dependencies.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,12 @@ class AppDependencies {
178178
);
179179
verificationCodeStorageService =
180180
InMemoryVerificationCodeStorageService();
181+
permissionService = const PermissionService();
181182
authService = AuthService(
182183
userRepository: userRepository,
183184
authTokenService: authTokenService,
184185
verificationCodeStorageService: verificationCodeStorageService,
186+
permissionService: permissionService,
185187
emailRepository: emailRepository,
186188
userAppSettingsRepository: userAppSettingsRepository,
187189
userContentPreferencesRepository: userContentPreferencesRepository,
@@ -193,9 +195,9 @@ class AppDependencies {
193195
topicRepository: topicRepository,
194196
sourceRepository: sourceRepository,
195197
);
196-
permissionService = const PermissionService();
197198
userPreferenceLimitService = DefaultUserPreferenceLimitService(
198199
remoteConfigRepository: remoteConfigRepository,
200+
permissionService: permissionService,
199201
log: Logger('DefaultUserPreferenceLimitService'),
200202
);
201203

lib/src/rbac/permissions.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,11 @@ abstract class Permissions {
5454
static const String remoteConfigRead = 'remote_config.read';
5555
static const String remoteConfigUpdate = 'remote_config.update';
5656
static const String remoteConfigDelete = 'remote_config.delete';
57+
58+
// Dashboard Permissions
59+
static const String dashboardLogin = 'dashboard.login';
60+
61+
// User Preference Permissions
62+
static const String userPreferenceBypassLimits =
63+
'user_preference.bypass_limits';
5764
}

lib/src/rbac/role_permissions.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ final Set<String> _dashboardPublisherPermissions = {
3333
Permissions.headlineCreate,
3434
Permissions.headlineUpdate,
3535
Permissions.headlineDelete,
36+
Permissions.dashboardLogin,
3637
};
3738

3839
final Set<String> _dashboardAdminPermissions = {
@@ -50,6 +51,7 @@ final Set<String> _dashboardAdminPermissions = {
5051
Permissions.remoteConfigCreate,
5152
Permissions.remoteConfigUpdate,
5253
Permissions.remoteConfigDelete,
54+
Permissions.userPreferenceBypassLimits,
5355
};
5456

5557
/// Defines the mapping between user roles (both app and dashboard) and the

lib/src/services/auth_service.dart

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:ht_api/src/rbac/permission_service.dart';
2+
import 'package:ht_api/src/rbac/permissions.dart';
13
import 'package:ht_api/src/services/auth_token_service.dart';
24
import 'package:ht_api/src/services/verification_code_storage_service.dart';
35
import 'package:ht_data_repository/ht_data_repository.dart';
@@ -21,12 +23,14 @@ class AuthService {
2123
required HtEmailRepository emailRepository,
2224
required HtDataRepository<UserAppSettings> userAppSettingsRepository,
2325
required HtDataRepository<UserContentPreferences>
24-
userContentPreferencesRepository,
26+
userContentPreferencesRepository,
27+
required PermissionService permissionService,
2528
required Uuid uuidGenerator,
2629
required Logger log,
2730
}) : _userRepository = userRepository,
2831
_authTokenService = authTokenService,
2932
_verificationCodeStorageService = verificationCodeStorageService,
33+
_permissionService = permissionService,
3034
_emailRepository = emailRepository,
3135
_userAppSettingsRepository = userAppSettingsRepository,
3236
_userContentPreferencesRepository = userContentPreferencesRepository,
@@ -39,7 +43,8 @@ class AuthService {
3943
final HtEmailRepository _emailRepository;
4044
final HtDataRepository<UserAppSettings> _userAppSettingsRepository;
4145
final HtDataRepository<UserContentPreferences>
42-
_userContentPreferencesRepository;
46+
_userContentPreferencesRepository;
47+
final PermissionService _permissionService;
4348
final Logger _log;
4449
final Uuid _uuid;
4550

@@ -77,13 +82,13 @@ class AuthService {
7782
);
7883
}
7984

80-
final hasRequiredRole =
81-
user.dashboardRole == DashboardUserRole.admin ||
82-
user.dashboardRole == DashboardUserRole.publisher;
83-
84-
if (!hasRequiredRole) {
85+
// Use the PermissionService to check for the specific dashboard login permission.
86+
if (!_permissionService.hasPermission(
87+
user,
88+
Permissions.dashboardLogin,
89+
)) {
8590
_log.warning(
86-
'Dashboard login failed: User ${user.id} lacks required roles.',
91+
'Dashboard login failed: User ${user.id} lacks required permission (${Permissions.dashboardLogin}).',
8792
);
8893
throw const ForbiddenException(
8994
'Your account does not have the required permissions to sign in.',
@@ -157,6 +162,24 @@ class AuthService {
157162
final existingUser = await _findUserByEmail(email);
158163
if (existingUser != null) {
159164
user = existingUser;
165+
// If this is a dashboard login, re-verify the user's dashboard role.
166+
// This closes the loophole where a non-admin user could request a code
167+
// via the app flow and then use it to log into the dashboard.
168+
if (isDashboardLogin) {
169+
if (!_permissionService.hasPermission(
170+
user,
171+
Permissions.dashboardLogin,
172+
)) {
173+
_log.warning(
174+
'Dashboard login failed: User ${user.id} lacks required permission '
175+
'during code verification.',
176+
);
177+
throw const ForbiddenException(
178+
'Your account does not have the required permissions to sign in.',
179+
);
180+
}
181+
_log.info('Dashboard user ${user.id} re-verified successfully.');
182+
}
160183
} else {
161184
// User not found.
162185
if (isDashboardLogin) {

lib/src/services/default_user_preference_limit_service.dart

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:ht_api/src/rbac/permission_service.dart';
2+
import 'package:ht_api/src/rbac/permissions.dart';
13
import 'package:ht_api/src/services/user_preference_limit_service.dart';
24
import 'package:ht_data_repository/ht_data_repository.dart';
35
import 'package:ht_shared/ht_shared.dart';
@@ -11,11 +13,14 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
1113
/// {@macro default_user_preference_limit_service}
1214
const DefaultUserPreferenceLimitService({
1315
required HtDataRepository<RemoteConfig> remoteConfigRepository,
16+
required PermissionService permissionService,
1417
required Logger log,
1518
}) : _remoteConfigRepository = remoteConfigRepository,
19+
_permissionService = permissionService,
1620
_log = log;
1721

1822
final HtDataRepository<RemoteConfig> _remoteConfigRepository;
23+
final PermissionService _permissionService;
1924
final Logger _log;
2025

2126
// Assuming a fixed ID for the RemoteConfig document
@@ -34,8 +39,11 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
3439
);
3540
final limits = remoteConfig.userPreferenceConfig;
3641

37-
// Admins have no limits.
38-
if (user.dashboardRole == DashboardUserRole.admin) {
42+
// Users with the bypass permission (e.g., admins) have no limits.
43+
if (_permissionService.hasPermission(
44+
user,
45+
Permissions.userPreferenceBypassLimits,
46+
)) {
3947
return;
4048
}
4149

@@ -94,8 +102,11 @@ class DefaultUserPreferenceLimitService implements UserPreferenceLimitService {
94102
);
95103
final limits = remoteConfig.userPreferenceConfig;
96104

97-
// Admins have no limits.
98-
if (user.dashboardRole == DashboardUserRole.admin) {
105+
// Users with the bypass permission (e.g., admins) have no limits.
106+
if (_permissionService.hasPermission(
107+
user,
108+
Permissions.userPreferenceBypassLimits,
109+
)) {
99110
return;
100111
}
101112

0 commit comments

Comments
 (0)