Skip to content

Commit 3613d43

Browse files
committed
feat(auth): add role-based permissions
- Added Role model with json serialization - Added Permission model with serialization - Replaced isAdmin with role in User model
1 parent 140f64e commit 3613d43

File tree

9 files changed

+122
-34
lines changed

9 files changed

+122
-34
lines changed

lib/ht_shared.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ library;
33

44
export 'src/exceptions/exceptions.dart';
55
export 'src/models/models.dart';
6+
export 'src/models/permission.dart';
7+
export 'src/models/role.dart';

lib/src/models/permission.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import 'package:equatable/equatable.dart';
2+
import 'package:json_annotation/json_annotation.dart';
3+
4+
part 'permission.g.dart';
5+
6+
/// {@template permission}
7+
/// Defines the available permissions in the system.
8+
///
9+
/// Permissions follow the format `resource.action`.
10+
/// {@endtemplate}
11+
@JsonSerializable()
12+
class Permission extends Equatable {
13+
/// {@macro permission}
14+
const Permission({required this.name});
15+
16+
/// Creates a Permission from JSON data.
17+
factory Permission.fromJson(Map<String, dynamic> json) =>
18+
_$PermissionFromJson(json);
19+
20+
/// The name of the permission (e.g., 'headline.read').
21+
final String name;
22+
23+
/// Converts this Permission instance to JSON data.
24+
Map<String, dynamic> toJson() => _$PermissionToJson(this);
25+
26+
@override
27+
List<Object> get props => [name];
28+
29+
@override
30+
String toString() => 'Permission(name: $name)';
31+
}

lib/src/models/permission.g.dart

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/models/role.dart

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'package:equatable/equatable.dart';
2+
import 'package:json_annotation/json_annotation.dart';
3+
4+
part 'role.g.dart';
5+
6+
/// {@template role}
7+
/// Defines the available user roles in the system.
8+
/// {@endtemplate}
9+
@JsonSerializable()
10+
class Role extends Equatable {
11+
/// {@macro role}
12+
const Role({required this.name});
13+
14+
/// Creates a Role from JSON data.
15+
factory Role.fromJson(Map<String, dynamic> json) => _$RoleFromJson(json);
16+
17+
/// The name of the role (e.g., 'admin', 'standard_user').
18+
final String name;
19+
20+
/// Converts this Role instance to JSON data.
21+
Map<String, dynamic> toJson() => _$RoleToJson(this);
22+
23+
@override
24+
List<Object> get props => [name];
25+
26+
@override
27+
String toString() => 'Role(name: $name)';
28+
}

lib/src/models/role.g.dart

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/models/user.dart

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ part 'user.g.dart';
1313
class User extends Equatable {
1414
/// Creates a new [User] instance.
1515
///
16-
/// Requires a unique [id], an [isAnonymous] flag, and an [isAdmin] flag.
17-
/// The [email] is optional and typically present only for non-anonymous
18-
/// users who have verified their email address.
16+
/// Requires a unique [id], an [isAnonymous] flag, and a [role].
17+
/// The [email] is optional and typically present only for non-anonymous
18+
/// users who have verified their email address.
1919
const User({
2020
required this.id,
2121
required this.isAnonymous,
22-
required this.isAdmin,
22+
required this.role,
2323
this.email,
2424
});
2525

@@ -41,17 +41,17 @@ class User extends Equatable {
4141
/// `false` otherwise.
4242
final bool isAnonymous;
4343

44-
/// Indicates whether the user has administrative privileges.
45-
final bool isAdmin;
44+
/// The role of the user (e.g., 'admin', 'standard_user').
45+
final String role;
4646

4747
/// Converts this User instance to JSON data.
4848
Map<String, dynamic> toJson() => _$UserToJson(this);
4949

5050
@override
51-
List<Object?> get props => [id, email, isAnonymous, isAdmin];
51+
List<Object?> get props => [id, email, isAnonymous, role];
5252

5353
@override
5454
String toString() {
55-
return 'User(id: $id, email: $email, isAnonymous: $isAnonymous, isAdmin: $isAdmin)';
55+
return 'User(id: $id, email: $email, isAnonymous: $isAnonymous, role: $role)';
5656
}
5757
}

lib/src/models/user.g.dart

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/src/models/auth_success_response_test.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ void main() {
88
id: 'user-123',
99
email: 'test@example.com',
1010
isAnonymous: false,
11-
isAdmin: false,
11+
role: 'standard_user',
1212
);
1313
const testToken = 'sample-jwt-token';
1414

@@ -96,7 +96,7 @@ void main() {
9696
id: 'user-456',
9797
email: 'updated@example.com',
9898
isAnonymous: true,
99-
isAdmin: false,
99+
role: 'standard_user',
100100
);
101101
final copiedResponse = authSuccessResponse.copyWith(user: updatedUser);
102102

@@ -120,7 +120,7 @@ void main() {
120120
const updatedUser = User(
121121
id: 'user-789',
122122
isAnonymous: true,
123-
isAdmin: false,
123+
role: 'standard_user',
124124
);
125125
const updatedToken = 'another-token-xyz';
126126
final copiedResponse = authSuccessResponse.copyWith(
@@ -146,7 +146,7 @@ void main() {
146146
const differentUser = User(
147147
id: 'diff-user',
148148
isAnonymous: false,
149-
isAdmin: false,
149+
role: 'admin',
150150
);
151151
const response1 = AuthSuccessResponse(user: testUser, token: testToken);
152152
const response2 = AuthSuccessResponse(

test/src/models/user_test.dart

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,50 @@ void main() {
88

99
test('supports value equality', () {
1010
expect(
11-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
11+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
1212
equals(
13-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
13+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
1414
),
1515
);
1616
expect(
17-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
17+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
1818
isNot(
1919
equals(
2020
const User(
2121
id: 'other-id',
2222
email: email,
2323
isAnonymous: false,
24-
isAdmin: false,
24+
role: 'standard_user',
2525
),
2626
),
2727
),
2828
);
2929
expect(
30-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
30+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
3131
isNot(
3232
equals(
3333
const User(
3434
id: id,
3535
email: 'other@example.com',
3636
isAnonymous: false,
37-
isAdmin: false,
37+
role: 'standard_user',
3838
),
3939
),
4040
),
4141
);
4242
expect(
43-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
43+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
4444
isNot(
4545
equals(
46-
const User(id: id, email: email, isAnonymous: true, isAdmin: false),
46+
const User(id: id, email: email, isAnonymous: true, role: 'standard_user'),
4747
),
4848
),
4949
);
50-
expect(
51-
const User(id: id, email: email, isAnonymous: false, isAdmin: false),
50+
expect(
51+
const User(id: id, email: email, isAnonymous: false, role: 'standard_user'),
5252
isNot(
5353
equals(
54-
const User(id: id, email: email, isAnonymous: false, isAdmin: true),
54+
const User(id: id, email: email, isAnonymous: false, role: 'admin'),
5555
),
5656
),
5757
);
@@ -63,19 +63,19 @@ void main() {
6363
id: id,
6464
email: email,
6565
isAnonymous: false,
66-
isAdmin: false,
66+
role: 'standard_user',
6767
).toString(),
6868
equals(
69-
'User(id: $id, email: $email, isAnonymous: false, isAdmin: false)',
69+
'User(id: $id, email: $email, isAnonymous: false, role: standard_user)',
7070
),
7171
);
7272
expect(
73-
const User(id: id, isAnonymous: true, isAdmin: false).toString(),
74-
equals('User(id: $id, email: null, isAnonymous: true, isAdmin: false)'),
73+
const User(id: id, isAnonymous: true, role: 'standard_user').toString(),
74+
equals('User(id: $id, email: null, isAnonymous: true, role: standard_user)'),
7575
);
7676
expect(
77-
const User(id: id, isAnonymous: false, isAdmin: true).toString(),
78-
equals('User(id: $id, email: null, isAnonymous: false, isAdmin: true)'),
77+
const User(id: id, isAnonymous: false, role: 'admin').toString(),
78+
equals('User(id: $id, email: null, isAnonymous: false, role: admin)'),
7979
);
8080
});
8181

@@ -85,13 +85,13 @@ void main() {
8585
id: id,
8686
email: email,
8787
isAnonymous: false,
88-
isAdmin: false,
88+
role: 'standard_user',
8989
);
9090
final json = user.toJson();
9191
final deserializedUser = User.fromJson(json);
9292
expect(deserializedUser, equals(user));
9393

94-
const anonUser = User(id: id, isAnonymous: true, isAdmin: false);
94+
const anonUser = User(id: id, isAnonymous: true, role: 'standard_user');
9595
final anonJson = anonUser.toJson();
9696
final deserializedAnonUser = User.fromJson(anonJson);
9797
expect(deserializedAnonUser, equals(anonUser));
@@ -100,7 +100,7 @@ void main() {
100100
id: id,
101101
email: email,
102102
isAnonymous: false,
103-
isAdmin: true,
103+
role: 'admin',
104104
);
105105
final adminJson = adminUser.toJson();
106106
final deserializedAdminUser = User.fromJson(adminJson);

0 commit comments

Comments
 (0)