Added account permissions and ui for recipient
This commit is contained in:
97
frontend/pweb/lib/providers/account.dart
Normal file
97
frontend/pweb/lib/providers/account.dart
Normal file
@@ -0,0 +1,97 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/api/requests/login_data.dart';
|
||||
import 'package:pshared/models/account/account.dart';
|
||||
import 'package:pshared/models/auth/login_outcome.dart';
|
||||
import 'package:pshared/models/auth/pending_login.dart';
|
||||
import 'package:pshared/models/describable.dart';
|
||||
|
||||
import 'package:pweb/providers/permissions.dart';
|
||||
import 'package:pweb/services/accounts.dart';
|
||||
|
||||
class AccountProvider extends ChangeNotifier {
|
||||
final AccountsService _accountsService;
|
||||
final PermissionsProvider _permissionsProvider;
|
||||
|
||||
AccountProvider({
|
||||
required AccountsService accountsService,
|
||||
required PermissionsProvider permissionsProvider,
|
||||
}) : _accountsService = accountsService,
|
||||
_permissionsProvider = permissionsProvider;
|
||||
|
||||
Account? _account;
|
||||
bool _isLoading = false;
|
||||
Object? _error;
|
||||
|
||||
Account? get account => _account;
|
||||
bool get isLoading => _isLoading;
|
||||
Object? get error => _error;
|
||||
bool get isLoggedIn => _account != null;
|
||||
|
||||
PendingLogin? get pendingLogin => null;
|
||||
|
||||
Future<LoginOutcome> login({
|
||||
required String email,
|
||||
required String password,
|
||||
required String locale,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
try {
|
||||
final result = await _accountsService.login(email, password, locale: locale);
|
||||
_account = result.account;
|
||||
_error = null;
|
||||
await _permissionsProvider.loadForAccount(result.account.id);
|
||||
return LoginOutcome.completed(result.account);
|
||||
} catch (e) {
|
||||
_error = e;
|
||||
rethrow;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
void completePendingLogin(Account account) {
|
||||
_account = account;
|
||||
_permissionsProvider.loadForAccount(account.id);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<Account?> restore() async {
|
||||
_setLoading(true);
|
||||
_account = null;
|
||||
_permissionsProvider.clear();
|
||||
_error = Exception('Сохраненная сессия отсутствует');
|
||||
_setLoading(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> signup({
|
||||
required AccountData account,
|
||||
required Describable organization,
|
||||
required String timezone,
|
||||
required Describable ownerRole,
|
||||
}) async {
|
||||
_setLoading(true);
|
||||
_error = null;
|
||||
try {
|
||||
await _accountsService.signup(account);
|
||||
} catch (e) {
|
||||
_error = e;
|
||||
rethrow;
|
||||
} finally {
|
||||
_setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
_account = null;
|
||||
_error = null;
|
||||
_permissionsProvider.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void _setLoading(bool value) {
|
||||
_isLoading = value;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
82
frontend/pweb/lib/providers/permissions.dart
Normal file
82
frontend/pweb/lib/providers/permissions.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:pshared/models/permissions/action.dart' as perm;
|
||||
import 'package:pshared/models/permissions/data/permission.dart';
|
||||
import 'package:pshared/models/permissions/descriptions/policy.dart';
|
||||
import 'package:pshared/models/permissions/effect.dart';
|
||||
import 'package:pshared/models/resources.dart';
|
||||
|
||||
import 'package:pweb/services/permissions.dart';
|
||||
import 'package:pweb/services/mock_ids.dart';
|
||||
|
||||
class PermissionsProvider extends ChangeNotifier {
|
||||
final PermissionsService _service;
|
||||
|
||||
PermissionsProvider({required PermissionsService service}) : _service = service;
|
||||
|
||||
bool _isLoading = false;
|
||||
Object? _error;
|
||||
String? _accountRef;
|
||||
bool _hasLoaded = false;
|
||||
String? _roleRef;
|
||||
List<Permission> _permissions = [];
|
||||
List<PolicyDescription> _policyDescriptions = [];
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
Object? get error => _error;
|
||||
bool get isReady => _hasLoaded && !_isLoading && _error == null;
|
||||
List<Permission> get permissions => List.unmodifiable(_permissions);
|
||||
bool get hasLoaded => _hasLoaded;
|
||||
|
||||
bool get isCompany => _roleRef == companyRoleId;
|
||||
bool get isRecipient => _roleRef == recipientRoleId;
|
||||
|
||||
Future<void> loadForAccount(String accountRef) async {
|
||||
_accountRef = accountRef;
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final access = await _service.loadForAccount(accountRef);
|
||||
_permissions = access.permissions.permissions;
|
||||
_policyDescriptions = access.descriptions.policies;
|
||||
_roleRef = access.permissions.roles.firstOrNull?.descriptionRef;
|
||||
} catch (e) {
|
||||
_permissions = [];
|
||||
_policyDescriptions = [];
|
||||
_error = e;
|
||||
_roleRef = null;
|
||||
} finally {
|
||||
_hasLoaded = true;
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_accountRef = null;
|
||||
_permissions = [];
|
||||
_policyDescriptions = [];
|
||||
_error = null;
|
||||
_hasLoaded = false;
|
||||
_roleRef = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool canAccessResource(ResourceType resource, {perm.Action? action}) {
|
||||
final policy = _policyDescriptions.firstWhereOrNull(
|
||||
(policy) => (policy.resourceTypes?.contains(resource) ?? false),
|
||||
);
|
||||
if (policy == null) return false;
|
||||
|
||||
return _permissions.any(
|
||||
(permission) =>
|
||||
permission.accountRef == _accountRef &&
|
||||
permission.policy.descriptionRef == policy.storable.id &&
|
||||
permission.policy.effect.effect == Effect.allow &&
|
||||
(action == null || permission.policy.effect.action == action),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:pshared/models/auth/pending_login.dart';
|
||||
import 'package:pshared/provider/account.dart';
|
||||
import 'package:pshared/service/account.dart';
|
||||
import 'package:pweb/services/auth.dart';
|
||||
|
||||
class TwoFactorProvider extends ChangeNotifier {
|
||||
static final _logger = Logger('provider.two_factor');
|
||||
final AccountProvider _accountProvider;
|
||||
final AuthenticationService _authService;
|
||||
|
||||
TwoFactorProvider({required AccountProvider accountProvider}) : _accountProvider = accountProvider;
|
||||
TwoFactorProvider({AuthenticationService? authService}) : _authService = authService ?? AuthenticationService();
|
||||
|
||||
bool _isSubmitting = false;
|
||||
bool _hasError = false;
|
||||
@@ -20,7 +18,6 @@ class TwoFactorProvider extends ChangeNotifier {
|
||||
bool get hasError => _hasError;
|
||||
bool get verificationSuccess => _verificationSuccess;
|
||||
String? get errorMessage => _errorMessage;
|
||||
PendingLogin? get pendingLogin => _accountProvider.pendingLogin;
|
||||
|
||||
|
||||
Future<void> submitCode(String code) async {
|
||||
@@ -31,16 +28,8 @@ class TwoFactorProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
|
||||
try {
|
||||
final pending = _accountProvider.pendingLogin;
|
||||
if (pending == null) {
|
||||
throw Exception('No pending login available');
|
||||
}
|
||||
final account = await AccountService.confirmLoginCode(
|
||||
pending: pending,
|
||||
code: code,
|
||||
);
|
||||
_accountProvider.completePendingLogin(account);
|
||||
_verificationSuccess = true;
|
||||
final isValid = await _authService.verifyTwoFactorCode(code);
|
||||
_verificationSuccess = isValid;
|
||||
} catch (e) {
|
||||
_hasError = true;
|
||||
_errorMessage = e.toString();
|
||||
@@ -52,18 +41,9 @@ class TwoFactorProvider extends ChangeNotifier {
|
||||
}
|
||||
|
||||
Future<void> resendCode() async {
|
||||
final pending = _accountProvider.pendingLogin;
|
||||
if (pending == null) {
|
||||
_logger.warning('No pending login to resend code for');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await AccountService.resendLoginCode(pending);
|
||||
} catch (e) {
|
||||
_logger.warning('Failed to resend login code', e);
|
||||
_hasError = true;
|
||||
_errorMessage = e.toString();
|
||||
notifyListeners();
|
||||
}
|
||||
_logger.fine('Resending mock two-factor code');
|
||||
_hasError = false;
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user