@@ -23,6 +23,7 @@ import 'package:uuid/uuid.dart';
2323import '../account/backend.dart' ;
2424import '../history/backend.dart' ;
2525import '../history/models.dart' ;
26+ import '../publisher/models.dart' ;
2627import '../shared/analyzer_client.dart' ;
2728import '../shared/configuration.dart' ;
2829import '../shared/dartdoc_client.dart' ;
@@ -283,9 +284,7 @@ class Backend {
283284 throw NotFoundException ('Package $package does not exists.' );
284285 }
285286 latestVersion = p.latestVersion;
286- if (! p.hasUploader (user.userId)) {
287- throw AuthorizationException .userIsNotAdminForPackage (package);
288- }
287+ await checkPackageAdmin (p, user.userId);
289288 p.isDiscontinued = options.isDiscontinued ?? p.isDiscontinued;
290289 _logger.info ('Updating $package options: '
291290 'isDiscontinued: ${p .isDiscontinued } '
@@ -297,6 +296,40 @@ class Backend {
297296 await analyzerClient.triggerAnalysis (package, latestVersion, < String > {});
298297 });
299298 }
299+
300+ /// Whether [userId] is a package admin (through direct uploaders list or
301+ /// publisher admin).
302+ ///
303+ /// Returns false if the user is not an admin.
304+ Future <bool > isPackageAdmin (models.Package p, String userId) async {
305+ if (userId == null ) {
306+ return false ;
307+ }
308+ if (p.publisherId == null ) {
309+ return p.containsUploader (userId);
310+ } else {
311+ final memberKey = db.emptyKey
312+ .append (Publisher , id: p.publisherId)
313+ .append (PublisherMember , id: userId);
314+ final list = await db.lookup <PublisherMember >([memberKey]);
315+ final member = list.single;
316+ return member? .role == PublisherMemberRole .admin;
317+ }
318+ }
319+
320+ /// Whether the [userId] is a package admin (through direct uploaders list or
321+ /// publisher admin).
322+ ///
323+ /// Throws AuthenticationException if the user is provided.
324+ /// Throws AuthorizationException if the user is not an admin for the package.
325+ Future checkPackageAdmin (models.Package package, String userId) async {
326+ if (userId == null ) {
327+ throw AuthenticationException .authenticationRequired ();
328+ }
329+ if (! await isPackageAdmin (package, userId)) {
330+ throw AuthorizationException .userIsNotAdminForPackage (package.name);
331+ }
332+ }
300333}
301334
302335/// Invalidate [cache] entries for given [package] .
@@ -503,11 +536,7 @@ class GCloudPackageRepository extends PackageRepository {
503536 if (package == null ) {
504537 _logger.info ('New package uploaded. [new-package-uploaded]' );
505538 package = _newPackageFromVersion (db, newVersion);
506- }
507-
508- // Check if the uploader of the new version is allowed to upload to
509- // the package.
510- if (! package.hasUploader (user.userId)) {
539+ } else if (! await backend.isPackageAdmin (package, user.userId)) {
511540 _logger.info ('User ${user .userId } (${user .email }) is not an uploader '
512541 'for package ${package .name }, rolling transaction back.' );
513542 await T .rollback ();
@@ -649,15 +678,15 @@ class GCloudPackageRepository extends PackageRepository {
649678 final packageKey = db.emptyKey.append (models.Package , id: packageName);
650679 final package = (await db.lookup ([packageKey])).first as models.Package ;
651680
652- _validatePackageUploader (packageName, package, user.userId);
681+ await _validatePackageUploader (packageName, package, user.userId);
653682
654683 if (! isValidEmail (uploaderEmail)) {
655684 throw GenericProcessingException (
656685 'Not a valid e-mail: `$uploaderEmail `.' );
657686 }
658687
659688 final uploader = await accountBackend.lookupUserByEmail (uploaderEmail);
660- if (uploader != null && package.hasUploader (uploader.userId)) {
689+ if (uploader != null && package.containsUploader (uploader.userId)) {
661690 // The requested uploaderEmail is already part of the uploaders.
662691 return ;
663692 }
@@ -709,13 +738,13 @@ class GCloudPackageRepository extends PackageRepository {
709738 final package = (await tx.lookup ([packageKey])).first as models.Package ;
710739
711740 try {
712- _validatePackageUploader (packageName, package, fromUserId);
741+ await _validatePackageUploader (packageName, package, fromUserId);
713742 } catch (_) {
714743 await tx.rollback ();
715744 rethrow ;
716745 }
717746
718- if (package.hasUploader (uploader.userId)) {
747+ if (package.containsUploader (uploader.userId)) {
719748 // The requested uploaderEmail is already part of the uploaders.
720749 await tx.rollback ();
721750 return ;
@@ -742,15 +771,15 @@ class GCloudPackageRepository extends PackageRepository {
742771 });
743772 }
744773
745- void _validatePackageUploader (
746- String packageName, models.Package package, String userId) {
774+ Future _validatePackageUploader (
775+ String packageName, models.Package package, String userId) async {
747776 // Fail if package doesn't exist.
748777 if (package == null ) {
749778 throw NotFoundException .resource (packageName);
750779 }
751780
752781 // Fail if calling user doesn't have permission to change uploaders.
753- if (! package. hasUploader ( userId)) {
782+ if (! await backend. isPackageAdmin (package, userId)) {
754783 throw AuthorizationException .userCannotChangeUploaders (package.name);
755784 }
756785 }
@@ -763,22 +792,12 @@ class GCloudPackageRepository extends PackageRepository {
763792 final packageKey = db.emptyKey.append (models.Package , id: packageName);
764793 final package = (await T .lookup ([packageKey])).first as models.Package ;
765794
766- // Fail if package doesn't exist.
767- if (package == null ) {
768- await T .rollback ();
769- throw NotFoundException .resource (packageName);
770- }
771-
772- // Fail if calling user doesn't have permission to change uploaders.
773- if (! package.hasUploader (user.userId)) {
774- await T .rollback ();
775- throw AuthorizationException .userCannotChangeUploaders (package.name);
776- }
795+ await _validatePackageUploader (packageName, package, user.userId);
777796
778797 final uploader =
779798 await accountBackend.lookupOrCreateUserByEmail (uploaderEmail);
780799 // Fail if the uploader we want to remove does not exist.
781- if (! package.hasUploader (uploader.userId)) {
800+ if (! package.containsUploader (uploader.userId)) {
782801 await T .rollback ();
783802 throw GenericProcessingException (
784803 'The uploader to remove does not exist.' );
0 commit comments