[haiku-depot-web] [haiku-depot-web-app] 9 new revisions pushed by haiku.li...@xxxxxxxxx on 2014-07-28 11:00 GMT

  • From: haiku-depot-web-app@xxxxxxxxxxxxxx
  • To: haiku-depot-web@xxxxxxxxxxxxx
  • Date: Mon, 28 Jul 2014 11:01:18 +0000

master moved from 7a57c0e8e047 to 61975821316a

9 new revisions:

Revision: 0c7928c87c68
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Thu Jul 24 11:35:00 2014 UTC
Log: + data / schema changes (simplification) related to authorization...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=0c7928c87c68

Revision: 4ab6d25e246d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Jul 26 04:02:29 2014 UTC
Log:      + german localization for authorization rules management...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=4ab6d25e246d

Revision: ed8ad4e8f4c6
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Jul 26 10:22:06 2014 UTC
Log: + possibility to download all of the authorization pkg rules as csv
http://code.google.com/p/haiku-depot-web-app/source/detail?r=ed8ad4e8f4c6

Revision: a2303a6006e2
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sun Jul 27 10:42:54 2014 UTC
Log: + documentation update in relation to authorization, but some other sm...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=a2303a6006e2

Revision: 64aa6420db79
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sun Jul 27 10:55:51 2014 UTC
Log:      + convert javascript throws from string to error object
http://code.google.com/p/haiku-depot-web-app/source/detail?r=64aa6420db79

Revision: 8b13fffe6056
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 09:04:02 2014 UTC
Log:      + add create / modify on pkg version localizations...
http://code.google.com/p/haiku-depot-web-app/source/detail?r=8b13fffe6056

Revision: 2ba59138f557
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 10:13:14 2014 UTC
Log:      + additional integration tests for authorization
http://code.google.com/p/haiku-depot-web-app/source/detail?r=2ba59138f557

Revision: 81ce1686ef67
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 10:34:56 2014 UTC
Log:      + small documentation correction
http://code.google.com/p/haiku-depot-web-app/source/detail?r=81ce1686ef67

Revision: 61975821316a
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 11:00:19 2014 UTC
Log:      + additional authorization test
http://code.google.com/p/haiku-depot-web-app/source/detail?r=61975821316a

==============================================================================
Revision: 0c7928c87c68
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Thu Jul 24 11:35:00 2014 UTC
Log:      + data / schema changes (simplification) related to authorization
+ user interface for editing package rules

http://code.google.com/p/haiku-depot-web-app/source/detail?r=0c7928c87c68

Added:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/AuthorizationApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationRuleConflictException.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationTargetScopeType.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationTargetType.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CheckAuthorizationRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CheckAuthorizationResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CreateAuthorizationPkgRuleRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CreateAuthorizationPkgRuleResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/RemoveAuthorizationPkgRuleRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/RemoveAuthorizationPkgRuleResult.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/SearchAuthorizationPkgRulesRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/SearchAuthorizationPkgRulesResult.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/AuthorizationApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/AuthorizationPkgRule.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/AuthorizationPkgRuleSearchSpecification.java /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.11__Authorization_2.sql
 /haikudepotserver-webapp/src/main/webapp/css/listauthorizationpkgrules.css
 /haikudepotserver-webapp/src/main/webapp/img/rowdelete.svg
/haikudepotserver-webapp/src/main/webapp/js/app/controller/addauthorizationpkgrule.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/addauthorizationpkgrulecontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrules.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrulescontroller.js /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java
Deleted:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/AuthorizationTargetType.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/CheckAuthorizationRequest.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/CheckAuthorizationResult.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionPublicRepository.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionUserRepository.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionPublicRepository.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionUserRepository.java
Modified:
/haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java
 /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf
 /haikudepotserver-docs/src/main/latex/docs/part-config.tex
 /haikudepotserver-parent/pom.xml
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Permission.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionUserPkg.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Repository.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Permission.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionUserPkg.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Repository.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_User.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/passwordreset/PasswordResetOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/Permission.java
 /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml
 /haikudepotserver-webapp/src/main/resources/messages.properties
 /haikudepotserver-webapp/src/main/webapp/css/haikudepotserver.css
 /haikudepotserver-webapp/src/main/webapp/js/app/constants.js
/haikudepotserver-webapp/src/main/webapp/js/app/controller/addeditrepositorycontroller.js
 /haikudepotserver-webapp/src/main/webapp/js/app/controller/createuser.html
/haikudepotserver-webapp/src/main/webapp/js/app/controller/createusercontroller.js
 /haikudepotserver-webapp/src/main/webapp/js/app/directive/banner.html
/haikudepotserver-webapp/src/main/webapp/js/app/directive/bannerdirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/modalcontainerdirective.js
 /haikudepotserver-webapp/src/main/webapp/js/app/routes.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbfactoryservice.js /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/jsonrpcservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/MiscelaneousApiIT.java

=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/AuthorizationApi.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.googlecode.jsonrpc4j.JsonRpcService;
+import org.haikuos.haikudepotserver.api1.model.authorization.*;
+import org.haikuos.haikudepotserver.api1.model.authorization.CreateAuthorizationPkgRuleRequest;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+
+/**
+ * <p>API related to authorization.</p>
+ */
+
+@JsonRpcService("/api/v1/authorization")
+public interface AuthorizationApi {
+
+    /**
+ * <p>This method will take in a list of permissions with targets and will return the list of those that + * pass authorization checks against the presently authenticated user.</p>
+     */
+
+ CheckAuthorizationResult checkAuthorization(CheckAuthorizationRequest deriveAuthorizationRequest);
+
+    /**
+ * <p>This method will create a new authorization rule. It will do this based on the data encapsulated in
+     * the request.</p>
+     */
+
+ CreateAuthorizationPkgRuleResult createAuthorizationPkgRule(CreateAuthorizationPkgRuleRequest request) throws ObjectNotFoundException, AuthorizationRuleConflictException;
+
+    /**
+ * <p>This method will delete an authorization rule identified by the coordinates in the request. If it + * was not able to find the rule to delete then it will thrown an instance of + * {@link org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException}.</p>
+     */
+
+ RemoveAuthorizationPkgRuleResult removeAuthorizationPkgRule(RemoveAuthorizationPkgRuleRequest request) throws ObjectNotFoundException;
+
+ SearchAuthorizationPkgRulesResult searchAuthorizationPkgRules(SearchAuthorizationPkgRulesRequest request) throws ObjectNotFoundException;
+
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationRuleConflictException.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+/**
+ * <p>This exception is thrown in the case where adding a new rule is going to conflict with
+ * another existing rule.</p>
+ */
+
+public class AuthorizationRuleConflictException extends Exception {
+
+    public AuthorizationRuleConflictException() {
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationTargetScopeType.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+public enum AuthorizationTargetScopeType {
+    PKG,
+    ALLPKGS
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/AuthorizationTargetType.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+/**
+ * <p>This enum is used to identify what type of target the authorization applies to.</p>
+ */
+
+public enum AuthorizationTargetType {
+    PKG,
+    USER,
+    REPOSITORY,
+    USERRATING
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CheckAuthorizationRequest.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+import java.util.List;
+
+public class CheckAuthorizationRequest {
+
+    public List<AuthorizationTargetAndPermission> targetAndPermissions;
+
+    public static class AuthorizationTargetAndPermission {
+
+        /**
+ * <p>The target type defines what sort of object you want to check your authorization for. The #targetIdentifier
+         * then identifies an instance of that type.</p>
+         */
+
+        public AuthorizationTargetType targetType;
+
+        /**
+ * This identifier will identify an instance of the #targetType that has the authorization applied to it. Some + * permissions may not require a target identifier; in which case this value can be supplied as null.
+         */
+
+        public String targetIdentifier;
+
+        /**
+ * <p>This is a list of permissions that the client would like to check for in the context of the target
+         * identified by other parameters in this request.</p>
+         */
+
+        public String permissionCode;
+
+        public AuthorizationTargetAndPermission() {
+        }
+
+ public AuthorizationTargetAndPermission(AuthorizationTargetType targetType, String targetIdentifier, String permissionCode) {
+            this.targetType = targetType;
+            this.targetIdentifier = targetIdentifier;
+            this.permissionCode = permissionCode;
+        }
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CheckAuthorizationResult.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+import java.util.List;
+
+public class CheckAuthorizationResult {
+
+    public List<AuthorizationTargetAndPermission> targetAndPermissions;
+
+    public static class AuthorizationTargetAndPermission {
+
+        /**
+ * <p>The target type defines what sort of object you want to check your authorization for. The #targetIdentifier
+         * then identifies an instance of that type.</p>
+         */
+
+        public AuthorizationTargetType targetType;
+
+        /**
+ * This identifier will identify an instance of the #targetType that has the authorization applied to it. Some + * permissions may not require a target identifier; in which case this value can be supplied as null.
+         */
+
+        public String targetIdentifier;
+
+        /**
+ * <p>This is a list of permissions that the client would like to check for in the context of the target
+         * identified by other parameters in this request.</p>
+         */
+
+        public String permissionCode;
+
+        /**
+ * <p>This boolean will be true if the target is authorized; false if not.</p>
+         */
+
+        public Boolean authorized;
+    }
+
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CreateAuthorizationPkgRuleRequest.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+public class CreateAuthorizationPkgRuleRequest {
+
+    public String userNickname;
+    public String pkgName;
+    public String permissionCode;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/CreateAuthorizationPkgRuleResult.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+public class CreateAuthorizationPkgRuleResult {
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/RemoveAuthorizationPkgRuleRequest.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+public class RemoveAuthorizationPkgRuleRequest {
+
+    public String userNickname;
+    public String pkgName;
+    public String permissionCode;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/RemoveAuthorizationPkgRuleResult.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+public class RemoveAuthorizationPkgRuleResult {
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/SearchAuthorizationPkgRulesRequest.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+import org.haikuos.haikudepotserver.api1.support.AbstractSearchRequest;
+
+import java.util.List;
+
+public class SearchAuthorizationPkgRulesRequest extends AbstractSearchRequest {
+
+    public String userNickname;
+
+    /**
+ * <p>If permission codes are supplied then only rules related to those permission codes will be shown. If this
+     * field is blank then all permissions will be considered.</p>
+     */
+
+    public List<String> permissionCodes;
+
+    public String pkgName;
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/authorization/SearchAuthorizationPkgRulesResult.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1.model.authorization;
+
+import org.haikuos.haikudepotserver.api1.support.AbstractSearchResult;
+
+public class SearchAuthorizationPkgRulesResult extends AbstractSearchResult<SearchAuthorizationPkgRulesResult.Rule> {
+
+    public static class Rule {
+
+        public String userNickname;
+        public String pkgName;
+        public String permissionCode;
+
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/AuthorizationApiImpl.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.api1.model.authorization.*;
+import org.haikuos.haikudepotserver.api1.support.AuthorizationFailureException;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+import org.haikuos.haikudepotserver.api1.support.ValidationException;
+import org.haikuos.haikudepotserver.api1.support.ValidationFailure;
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.AuthorizationPkgRuleOrchestrationService;
+import org.haikuos.haikudepotserver.security.AuthorizationService;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRuleSearchSpecification;
+import org.haikuos.haikudepotserver.security.model.Permission;
+import org.haikuos.haikudepotserver.security.model.TargetType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@Component
+public class AuthorizationApiImpl extends AbstractApiImpl implements AuthorizationApi {
+
+ protected static Logger logger = LoggerFactory.getLogger(AuthorizationApiImpl.class);
+
+    @Resource
+    ServerRuntime serverRuntime;
+
+    @Resource
+    AuthorizationService authorizationService;
+
+    @Resource
+ AuthorizationPkgRuleOrchestrationService authorizationRulesOrchestrationService;
+
+    // -------------------------------
+    // HELPERS
+
+ private org.haikuos.haikudepotserver.dataobjects.Permission ensurePermission(ObjectContext context, String code) throws ObjectNotFoundException {
+
+ Optional<org.haikuos.haikudepotserver.dataobjects.Permission> permissionOptional = org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, code);
+
+        if(!permissionOptional.isPresent()) {
+            throw new ObjectNotFoundException(
+ org.haikuos.haikudepotserver.dataobjects.Permission.class.getSimpleName(),
+                    code);
+        }
+
+        return permissionOptional.get();
+    }
+
+ private Pkg ensurePkg(ObjectContext context, String name) throws ObjectNotFoundException {
+        Optional<Pkg> pkgOptional = Pkg.getByName(context, name);
+
+        if(!pkgOptional.isPresent()) {
+ throw new ObjectNotFoundException(Pkg.class.getSimpleName(), name);
+        }
+
+        return pkgOptional.get();
+    }
+
+ private User ensureUser(ObjectContext context, String nickname) throws ObjectNotFoundException { + Optional<User> userOptional = User.getByNickname(context, nickname);
+
+        if(!userOptional.isPresent()) {
+ throw new ObjectNotFoundException(User.class.getSimpleName(), nickname);
+        }
+
+        return userOptional.get();
+    }
+
+    /**
+ * <P>Checks that the currently authenticated user is able to manipulate the authorization configuration
+     * of the system.</P>
+     */
+
+    private void ensureCanAuthorizationManipulate() {
+        ObjectContext context = serverRuntime.getContext();
+        User authUser = obtainAuthenticatedUser(context);
+
+ if(!authorizationService.check(context, authUser, null, Permission.AUTHORIZATION_CONFIGURE)) {
+            throw new AuthorizationFailureException();
+        }
+    }
+
+    // -------------------------------
+    // API
+
+    @Override
+ public CheckAuthorizationResult checkAuthorization(CheckAuthorizationRequest deriveAuthorizationRequest) {
+
+        Preconditions.checkNotNull(deriveAuthorizationRequest);
+ Preconditions.checkNotNull(deriveAuthorizationRequest.targetAndPermissions);
+
+        final ObjectContext context = serverRuntime.getContext();
+        CheckAuthorizationResult result = new CheckAuthorizationResult();
+        result.targetAndPermissions = Lists.newArrayList();
+
+ for(CheckAuthorizationRequest.AuthorizationTargetAndPermission targetAndPermission : deriveAuthorizationRequest.targetAndPermissions) {
+
+ CheckAuthorizationResult.AuthorizationTargetAndPermission authorizationTargetAndPermission = new CheckAuthorizationResult.AuthorizationTargetAndPermission();
+
+ authorizationTargetAndPermission.permissionCode = targetAndPermission.permissionCode; + authorizationTargetAndPermission.targetIdentifier = targetAndPermission.targetIdentifier; + authorizationTargetAndPermission.targetType = targetAndPermission.targetType;
+
+ authorizationTargetAndPermission.authorized = authorizationService.check(
+                    context,
+                    tryObtainAuthenticatedUser(context).orNull(),
+ null!=targetAndPermission.targetType ? TargetType.valueOf(targetAndPermission.targetType.name()) : null,
+                    targetAndPermission.targetIdentifier,
+ Permission.valueOf(targetAndPermission.permissionCode));
+
+ result.targetAndPermissions.add(authorizationTargetAndPermission);
+        }
+
+        return result;
+    }
+
+    @Override
+ public CreateAuthorizationPkgRuleResult createAuthorizationPkgRule(CreateAuthorizationPkgRuleRequest request) throws ObjectNotFoundException,AuthorizationRuleConflictException {
+
+        Preconditions.checkNotNull(request);
+ Preconditions.checkState(!Strings.isNullOrEmpty(request.permissionCode), "the permission code is required"); + Preconditions.checkState(Permission.valueOf(request.permissionCode.toUpperCase()).getRequiredTargetType() == TargetType.PKG,"the permission should have a target type of; " + TargetType.PKG); + Preconditions.checkState(!Strings.isNullOrEmpty(request.userNickname),"the user nickname must be supplied");
+
+        ensureCanAuthorizationManipulate();
+        ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Permission permission = ensurePermission(context, request.permissionCode);
+        User user = ensureUser(context, request.userNickname);
+
+        if(user.getIsRoot()) {
+ throw new ValidationException(new ValidationFailure("user", "root"));
+        }
+
+        Pkg pkg = null;
+
+        if(null!=request.pkgName) {
+            pkg = ensurePkg(context, request.pkgName);
+        }
+
+ // now we need to check to make sure that the newly added rule does not conflict with an existing
+        // rule.  If this is the case then exception.
+
+ if(authorizationRulesOrchestrationService.wouldConflict(context,user,permission,pkg)) {
+            throw new AuthorizationRuleConflictException();
+        }
+
+        authorizationRulesOrchestrationService.create(
+                context, user,
+                permission,
+                pkg);
+
+        context.commitChanges();
+
+        return new CreateAuthorizationPkgRuleResult();
+    }
+
+    @Override
+ public RemoveAuthorizationPkgRuleResult removeAuthorizationPkgRule(RemoveAuthorizationPkgRuleRequest request) throws ObjectNotFoundException {
+
+        Preconditions.checkNotNull(request);
+ Preconditions.checkState(!Strings.isNullOrEmpty(request.permissionCode), "the permission code is required"); + Preconditions.checkState(!Strings.isNullOrEmpty(request.userNickname),"the user nickname is required");
+
+        ensureCanAuthorizationManipulate();
+        ObjectContext context = serverRuntime.getContext();
+ org.haikuos.haikudepotserver.dataobjects.Permission permission = ensurePermission(context, request.permissionCode);
+        User user = null;
+
+        if(null!=request.userNickname) {
+            user = ensureUser(context, request.userNickname);
+        }
+
+        Pkg pkg = null;
+
+        if(!Strings.isNullOrEmpty(request.pkgName)) {
+            pkg = ensurePkg(context, request.pkgName);
+        }
+
+        authorizationRulesOrchestrationService.remove(
+                context,
+                user,
+                permission,
+                pkg);
+
+        context.commitChanges();
+
+        return new RemoveAuthorizationPkgRuleResult();
+    }
+
+    @Override
+ public SearchAuthorizationPkgRulesResult searchAuthorizationPkgRules(SearchAuthorizationPkgRulesRequest request) throws ObjectNotFoundException {
+
+        Preconditions.checkNotNull(request);
+        Preconditions.checkState(null==request.limit || request.limit > 0);
+ Preconditions.checkState(null!=request.offset && request.offset >= 0);
+
+        ensureCanAuthorizationManipulate();
+
+        final ObjectContext context = serverRuntime.getContext();
+
+ AuthorizationPkgRuleSearchSpecification specification = new AuthorizationPkgRuleSearchSpecification();
+
+        specification.setLimit(request.limit);
+        specification.setOffset(request.offset);
+        specification.setIncludeInactive(false);
+
+        if(!Strings.isNullOrEmpty(request.userNickname)) {
+ specification.setUser(ensureUser(context, request.userNickname));
+        }
+
+        if(null!=request.permissionCodes) {
+ List<org.haikuos.haikudepotserver.dataobjects.Permission> permissions = Lists.newArrayList();
+
+            for(int i=0;i<request.permissionCodes.size();i++) {
+ permissions.add(ensurePermission(context, request.permissionCodes.get(i)));
+            }
+
+            specification.setPermissions(permissions);
+        }
+
+        if(!Strings.isNullOrEmpty(request.pkgName)) {
+            specification.setPkg(ensurePkg(context, request.pkgName));
+        }
+
+ SearchAuthorizationPkgRulesResult result = new SearchAuthorizationPkgRulesResult(); + result.total = authorizationRulesOrchestrationService.total(context, specification);
+        result.items = Lists.transform(
+ authorizationRulesOrchestrationService.search(context, specification), + new Function<AuthorizationPkgRule, SearchAuthorizationPkgRulesResult.Rule>() {
+                    @Override
+ public SearchAuthorizationPkgRulesResult.Rule apply(AuthorizationPkgRule input) { + SearchAuthorizationPkgRulesResult.Rule rule = new SearchAuthorizationPkgRulesResult.Rule(); + rule.permissionCode = input.getPermission().getCode();
+                        rule.userNickname = input.getUser().getNickname();
+ rule.pkgName = null!=input.getPkg() ? input.getPkg().getName() : null;
+                        return rule;
+                    }
+                }
+        );
+
+        return result;
+    }
+
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.security;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.exp.Expression;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.query.EJBQLQuery;
+import org.apache.cayenne.query.SelectQuery;
+import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRuleSearchSpecification;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <p>This service is designed to orchestrate the authorization rules in the system.</p>
+ */
+
+@Service
+public class AuthorizationPkgRuleOrchestrationService {
+
+ protected static Logger logger = LoggerFactory.getLogger(AuthorizationPkgRuleOrchestrationService.class);
+
+    @SuppressWarnings("UnusedParameters")
+    private String prepareWhereClause(
+            List<Object> parameterAccumulator,
+            ObjectContext context,
+            AuthorizationPkgRuleSearchSpecification specification) {
+
+        List<String> clauses = Lists.newArrayList();
+
+        if(null!= specification.getPermissions()) {
+
+            StringBuilder query = new StringBuilder();
+            query.append('(');
+
+            for(int i=0;i< specification.getPermissions().size();i++) {
+                if(0!=i) {
+                    query.append(" OR ");
+                }
+
+ parameterAccumulator.add(specification.getPermissions().get(i));
+                query.append("r.permission=?");
+ query.append(Integer.toString(parameterAccumulator.size()));
+            }
+
+            query.append(')');
+
+            clauses.add(query.toString());
+        }
+
+        if(null!= specification.getUser()) {
+            parameterAccumulator.add(specification.getUser());
+            clauses.add("r.user=?"+parameterAccumulator.size());
+
+            if(!specification.getIncludeInactive()) {
+                clauses.add("r.user."+User.ACTIVE_PROPERTY+"=true");
+            }
+        }
+
+        if(null!=specification.getPkg()) {
+            parameterAccumulator.add(specification.getPkg());
+            clauses.add("r.pkg=?"+parameterAccumulator.size());
+
+            if(!specification.getIncludeInactive()) {
+                clauses.add("r.pkg."+Pkg.ACTIVE_PROPERTY+"=true");
+            }
+        }
+
+        return Joiner.on(" AND ").join(clauses);
+    }
+
+    public List<AuthorizationPkgRule> search(
+            ObjectContext context,
+            AuthorizationPkgRuleSearchSpecification specification) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(specification);
+        Preconditions.checkState(specification.getOffset() >= 0);
+        Preconditions.checkState(specification.getLimit() > 0);
+
+        // special case.
+
+ if(null!=specification.getPermissions() && specification.getPermissions().isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        StringBuilder queryBuilder = new StringBuilder();
+        List<Object> parameterAccumulator = Lists.newArrayList();
+ String whereClause = prepareWhereClause(parameterAccumulator, context, specification);
+
+        queryBuilder.append("SELECT r FROM ");
+        queryBuilder.append(PermissionUserPkg.class.getSimpleName());
+        queryBuilder.append(" AS r");
+
+        if(whereClause.length() > 0) {
+            queryBuilder.append(" WHERE ");
+            queryBuilder.append(whereClause);
+        }
+
+        queryBuilder.append(" ORDER BY r.createTimestamp DESC");
+
+        EJBQLQuery query = new EJBQLQuery(queryBuilder.toString());
+
+        for(int i=0;i<parameterAccumulator.size();i++) {
+            query.setParameter(i+1,parameterAccumulator.get(i));
+        }
+
+        query.setFetchLimit(specification.getLimit());
+        query.setFetchOffset(specification.getOffset());
+
+        //noinspection unchecked
+        return (List<AuthorizationPkgRule>) context.performQuery(query);
+    }
+
+    public long total(
+            ObjectContext context,
+            AuthorizationPkgRuleSearchSpecification specification) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(specification);
+
+ if(null!=specification.getPermissions() && specification.getPermissions().isEmpty()) {
+            return 0;
+        }
+
+        StringBuilder queryBuilder = new StringBuilder();
+        List<Object> parameterAccumulator = Lists.newArrayList();
+ String whereClause = prepareWhereClause(parameterAccumulator, context, specification);
+
+        queryBuilder.append("SELECT COUNT(r) FROM ");
+        queryBuilder.append(PermissionUserPkg.class.getSimpleName());
+        queryBuilder.append(" AS r");
+
+        if(whereClause.length() > 0) {
+            queryBuilder.append(" WHERE ");
+            queryBuilder.append(whereClause);
+        }
+
+        EJBQLQuery query = new EJBQLQuery(queryBuilder.toString());
+
+        for(int i=0;i<parameterAccumulator.size();i++) {
+            query.setParameter(i+1,parameterAccumulator.get(i));
+        }
+
+ @SuppressWarnings("unchecked") List<Number> result = context.performQuery(query);
+
+        switch(result.size()) {
+            case 1:
+                return result.get(0).longValue();
+
+            default:
+ throw new IllegalStateException("expected 1 row from count query, but got "+result.size());
+        }
+    }
+
+    /**
+ * <p>This method returns true if the proposed new rule would conflict with existing rules
+     * that are already present in the system.</p>
+     */
+
+    public boolean wouldConflict(
+            ObjectContext context,
+            User user,
+            Permission permission,
+            Pkg pkg) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(permission);
+        Preconditions.checkNotNull(user);
+
+ Expression baseE = ExpressionFactory.matchExp(PermissionUserPkg.USER_PROPERTY, user) + .andExp(ExpressionFactory.matchExp(PermissionUserPkg.PERMISSION_PROPERTY, permission));
+
+        {
+            SelectQuery query = new SelectQuery(
+                    PermissionUserPkg.class,
+ baseE.andExp(ExpressionFactory.matchExp(PermissionUserPkg.PKG_PROPERTY, null)));
+
+            if (!context.performQuery(query).isEmpty()) {
+                return true;
+            }
+        }
+
+        {
+            SelectQuery query = new SelectQuery(
+                    PermissionUserPkg.class,
+ baseE.andExp(ExpressionFactory.matchExp(PermissionUserPkg.PKG_PROPERTY, pkg)));
+
+            if (!context.performQuery(query).isEmpty()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+ * <p>This method will create a new rule object. It will decide what sort of object to create
+     * based on the inputs supplied.</p>
+     */
+
+    public AuthorizationPkgRule create(
+            ObjectContext context,
+            User user,
+            Permission permission,
+            Pkg pkg) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(permission);
+        Preconditions.checkNotNull(user);
+
+        if(user.getIsRoot()) {
+ throw new IllegalStateException("when creating an authorization rule, the rule is not able to be applied to a root user");
+        }
+
+ PermissionUserPkg rule = context.newObject(PermissionUserPkg.class);
+        rule.setPermission(permission);
+        rule.setUser(user);
+        rule.setPkg(pkg);
+ logger.info("did create permission user repository; {},{},{}",permission,user,pkg);
+
+        return rule;
+    }
+
+    public void remove(
+            ObjectContext context,
+            User user,
+            Permission permission,
+            Pkg pkg) {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(permission);
+        Preconditions.checkNotNull(user);
+
+ Optional<PermissionUserPkg> permissionUserPkgOptional = PermissionUserPkg.getByPermissionUserAndPkg(context, permission, user, pkg);
+
+        if (permissionUserPkgOptional.isPresent()) {
+            context.deleteObjects(permissionUserPkgOptional.get());
+ logger.info("did remove permission user package; {},{},{}",permission,user,pkg);
+        } else {
+ logger.info("no permission user package already existed to remove; {},{},{}",permission,user,pkg);
+        }
+
+    }
+
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/AuthorizationPkgRule.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.security.model;
+
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.User;
+
+public interface AuthorizationPkgRule {
+
+    org.haikuos.haikudepotserver.dataobjects.Permission getPermission();
+
+    User getUser();
+
+    Pkg getPkg();
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/model/AuthorizationPkgRuleSearchSpecification.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.security.model;
+
+import org.haikuos.haikudepotserver.dataobjects.Permission;
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.support.AbstractSearchSpecification;
+
+import java.util.List;
+
+public class AuthorizationPkgRuleSearchSpecification extends AbstractSearchSpecification {
+
+    private User user;
+
+    /**
+ * <p>If permission codes are supplied then only rules related to those permission codes will be shown. If this
+     * field is blank then all permissions will be considered.</p>
+     */
+
+ private List<org.haikuos.haikudepotserver.dataobjects.Permission> permissions;
+
+    private Pkg pkg;
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
+
+    public List<Permission> getPermissions() {
+        return permissions;
+    }
+
+    public void setPermissions(List<Permission> permissions) {
+        this.permissions = permissions;
+    }
+
+    public Pkg getPkg() {
+        return pkg;
+    }
+
+    public void setPkg(Pkg pkg) {
+        this.pkg = pkg;
+    }
+
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.11__Authorization_2.sql Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,27 @@
+
+-- ------------------------------------------------------
+-- PERMISSIONS WE DON'T NEED
+-- ------------------------------------------------------
+
+DELETE FROM haikudepot.permission WHERE code NOT IN (
+    'pkg_editicon',
+    'pkg_editscreenshot',
+    'pkg_editcategories',
+    'pkg_editversionlocalization');
+
+-- ------------------------------------------------------
+-- CHANGE OF PLAN - DON'T NEED PUBLIC PERMISSIONS OR
+-- REPOSITORY PERMISSIONS EITHER
+-- ------------------------------------------------------
+
+DROP SEQUENCE haikudepot.permission_public_repository_seq;
+DROP TABLE haikudepot.permission_public_repository;
+DROP SEQUENCE haikudepot.permission_user_repository_seq;
+DROP TABLE haikudepot.permission_user_repository;
+
+-- ------------------------------------------------------
+-- CREATE TIMESTAMPS ON RULES
+-- ------------------------------------------------------
+
+ALTER TABLE haikudepot.permission_user_pkg ADD COLUMN create_timestamp TIMESTAMP NOT NULL; +ALTER TABLE haikudepot.permission_user_pkg ALTER COLUMN pkg_id DROP NOT NULL;
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/css/listauthorizationpkgrules.css Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,4 @@
+.list-authorization-pkg-rules .table-general > tbody > tr > td:nth-child(4) {
+    width: 16px;
+    text-align: center;
+}
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/img/rowdelete.svg Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns="http://www.w3.org/2000/svg";
+   version="1.1"
+   width="12"
+   height="12">
+<path stroke-linecap="butt" stroke-width="2" stroke="black" fill="none" d="M3.5 3.5 L8.5 8.5 M3.5 8.5 L 8.5 3.5"/> +<circle stroke-width="1" stroke="black" fill="none" cx="6.0" cy="6.0" r="5.5"/>
+</svg>
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/addauthorizationpkgrule.html Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,80 @@
+<breadcrumbs items="breadcrumbItems"></breadcrumbs>
+
+<div class="content-container">
+
+    <div class="form-alert-container" ng-show="wasConflicting">
+ <strong><message key="addAuthorizationPkgRule.conflict.title"></message></strong> + <message key="addAuthorizationPkgRule.conflict.description"></message>
+    </div>
+
+    <form name="addRuleForm" novalidate="novalidate">
+
+ <label for="userNickname"><message key="addAuthorizationPkgRule.userNickname.title"></message></label> + <div class="form-control-group" ng-class="deriveFormControlsContainerClasses('user')">
+            <input
+                    id="userNickname"
+                    type="text"
+                    name="userNickname"
+                    autocomplete="off"
+                    placeholder="erik"
+                    ng-model="workingRule.userNickname"
+                    ng-required="true"
+                    ng-change="userNicknameDidChange()"
+                    ng-pattern="{{userNicknamePattern}}"></input>
+ <error-messages key-prefix="addAuthorizationPkgRule.userNickname" error="addRuleForm.userNickname.$error"></error-messages>
+        </div>
+
+ <label for="permission"><message key="addAuthorizationPkgRule.permission.title"></message></label>
+        <div class="form-control-group">
+            <select
+                    name="permission"
+                    id="permission"
+                    required="true"
+                    ng-model="workingRule.permission"
+ ng-options="aPermission.title for aPermission in allPermissions">
+            </select>
+        </div>
+
+ <label><message key="addAuthorizationPkgRule.authorizationTargetScopeType.title"></message></label>
+        <div class="form-control-group">
+ <input type="radio" name="authorizationTargetScopeType" value="APKG" ng-model="workingRule.authorizationTargetScopeType"> + <message key="addAuthorizationPkgRule.authorizationTargetScopeType.apkg.title"></message>
+            &nbsp;&nbsp;
+ <input type="radio" name="authorizationTargetScopeType" value="ALLPKGS" ng-model="workingRule.authorizationTargetScopeType"> + <message key="addAuthorizationPkgRule.authorizationTargetScopeType.allpkgs.title"></message>
+        </div>
+
+ <label ng-show="'APKG'==workingRule.authorizationTargetScopeType" for="pkgName">
+            <message key="addAuthorizationPkgRule.pkgName.title"></message>
+        </label>
+        <div
+                ng-show="'APKG'==workingRule.authorizationTargetScopeType"
+                class="form-control-group"
+                ng-class="deriveFormControlsContainerClasses('user')">
+            <input
+                    id="pkgName"
+                    type="text"
+                    name="pkgName"
+                    autocomplete="off"
+                    placeholder="myapplication"
+                    ng-model="workingRule.pkgName"
+ ng-required="'APKG'==workingRule.authorizationTargetScopeType"
+                    ng-change="pkgNameDidChange()"
+                    ng-pattern="{{pkgNamePattern}}"></input>
+ <error-messages key-prefix="addAuthorizationPkgRule.pkgName" error="addRuleForm.pkgName.$error"></error-messages>
+        </div>
+
+        <div class="form-action-container">
+            <button
+                    ng-disabled="addRuleForm.$invalid"
+                    ng-click="goAddRule()"
+                    type="submit"
+ class="main-action"><message key="addAuthorizationPkgRule.action.title"></message></button>
+        </div>
+
+    </form>
+</div>
+
+<div class="footer"></div>
+<spinner spin="shouldSpin()"></spinner>
+
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/addauthorizationpkgrulecontroller.js Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+angular.module('haikudepotserver').controller(
+    'AddAuthorizationPkgRuleController',
+    [
+        '$scope','$log',
+        
'jsonRpc','constants','breadcrumbs','breadcrumbFactory','errorHandling','messageSource',
+        'userState',
+        function(
+            $scope,$log,
+ jsonRpc,constants,breadcrumbs,breadcrumbFactory,errorHandling,messageSource,
+            userState) {
+
+            $scope.pkgNamePattern = '' + constants.PATTERN_PKG_NAME;
+ $scope.userNicknamePattern = '' + constants.PATTERN_USER_NICKNAME;
+
+ // This var will be true if the rule that the operator was attempting to create conflicted
+            // with an existing rule that was already there.
+
+            $scope.wasConflicting = false;
+
+ // It seems a bit poor to hard code these permissions, but we actually only want to allow certain
+            // permissions to be used here.
+
+            $scope.allPermissions = _.map(
+                [
+                    'pkg_editicon',
+                    'pkg_editscreenshot',
+                    'pkg_editcategories',
+                    'pkg_editversionlocalization'
+                ],
+                function(permissionCode) {
+                    var result = {
+                        code : permissionCode,
+                        title : '...'
+                    };
+
+ messageSource.get(userState.naturalLanguageCode(),'permission.' + permissionCode + '.title').then(
+                        function(value) {
+                            result.title = value;
+                        },
+                        function() {
+                            result.title = '???';
+                        }
+                    );
+
+                    return result;
+                }
+            );
+
+            $scope.workingRule = {
+                userNickname : undefined,
+                permission : $scope.allPermissions[0],
+                authorizationTargetScopeType : 'APKG'
+            };
+
+            var amSaving = false;
+
+            $scope.shouldSpin = function() {
+                return amSaving;
+            };
+
+            $scope.deriveFormControlsContainerClasses = function(name) {
+ return $scope.addRuleForm[name] && $scope.addRuleForm[name].$invalid ? ['form-control-group-error'] : [];
+            };
+
+            /**
+ * <p>This function gets hit when the user nickname changes. It may be that a prior attempt to create + * the rule was using a user nickname that did not exist etc... and so this will create validation + * failures that cannot be tested until the user submits the form. To allow them to try again, any
+             * mutation of the data will reset the validation.</p>
+             */
+
+            $scope.userNicknameDidChange = function() {
+                $scope.addRuleForm.userNickname.$setValidity('root',true);
+ $scope.addRuleForm.userNickname.$setValidity('notfound',true);
+            };
+
+            $scope.pkgNameDidChange = function() {
+                $scope.addRuleForm.pkgName.$setValidity('notfound',true);
+            };
+
+            function refreshBreadcrumbItems() {
+                breadcrumbs.mergeCompleteStack([
+                    breadcrumbFactory.createHome(),
+                    breadcrumbFactory.createListAuthorizationPkgRules(),
+                    breadcrumbFactory.createAddAuthorizationPkgRule()
+                ]);
+            }
+
+            refreshBreadcrumbItems();
+
+            /**
+ * <p>This function gets hit when the user chooses to save a rule.</p>
+             */
+
+            $scope.goAddRule = function() {
+
+                amSaving = true;
+                $scope.wasConflicting = false;
+
+                if($scope.addRuleForm.$invalid) {
+ throw Error('it is not possible to add a rule if the form is invalid.');
+                }
+
+                var request = {
+                    userNickname : $scope.workingRule.userNickname,
+                    permissionCode : $scope.workingRule.permission.code,
+                    pkgName : undefined
+                };
+
+ if('APKG' == $scope.workingRule.authorizationTargetScopeType) {
+                    request.pkgName = $scope.workingRule.pkgName;
+                }
+
+                jsonRpc.call(
+                    constants.ENDPOINT_API_V1_AUTHORIZATION,
+                    "createAuthorizationPkgRule",
+                    [request]
+                ).then(
+                    function() {
+                        $log.info('did create authorization pkg rule');
+                        breadcrumbs.popAndNavigate();
+                    },
+                    function(err) {
+
+                        switch(err.code) {
+
+ case jsonRpc.errorCodes.AUTHORIZATIONRULECONFLICT:
+                                $scope.wasConflicting = true;
+                                break;
+
+                            case jsonRpc.errorCodes.VALIDATION:
+                                _.each(
+                                    err.data.validationfailures,
+                                    function(item) {
+                                        if('user'==item.property) {
+ $scope.addRuleForm.userNickname.$setValidity(item.message,false);
+                                        }
+                                        else {
+ errorHandling.handleJsonRpcError(err);
+                                        }
+                                    }
+                                );
+                                break;
+
+                            case jsonRpc.errorCodes.OBJECTNOTFOUND:
+
+                                switch(err.data.entityName) {
+
+                                    case 'User':
+ $scope.addRuleForm.userNickname.$setValidity('notfound',false);
+                                        break;
+
+                                    case 'Pkg':
+ $scope.addRuleForm.pkgName.$setValidity('notfound',false);
+                                        break;
+
+                                    default:
+ errorHandling.handleJsonRpcError(err);
+                                        break;
+
+                                }
+
+                                break;
+
+                            default:
+                                errorHandling.handleJsonRpcError(err);
+                                break;
+
+                        }
+
+                    }
+                )
+                    .finally(function() {
+                        amSaving = false;
+                    });
+
+            }
+
+        }
+    ]
+);
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrules.html Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,127 @@
+<breadcrumbs items="breadcrumbItems"></breadcrumbs>
+
+<div class="content-container list-authorization-pkg-rules">
+
+    <!-- SEARCH -->
+
+    <div id="search-criteria-container">
+        <div>
+            <label for="user-nickname">
+ <message key="listAuthorizationPkgRules.search.userNickname.title"></message>
+            </label>
+            <input
+                    id="user-nickname"
+                    ng-model="criteria.userNickname"
+                    autocomplete="off"
+                    ng-maxlength="48"
+                    return-key-press="goSearch()"
+                    ng-change="userNicknameDidChange()"
+                    placeholder="erik"></input>
+        </div>
+        <div>
+            <label for="user-nickname">
+ <message key="listAuthorizationPkgRules.search.pkgName.title"></message>
+            </label>
+            <input
+                    ng-model="criteria.pkgName"
+                    autocomplete="off"
+                    ng-maxlength="48"
+                    return-key-press="goSearch()"
+                    ng-change="pkgNameDidChange()"
+                    placeholder="myapplication"></input>
+        </div>
+        <div>
+            <button ng-click="goSearch()">
+ <message key="listAuthorizationPkgRules.searchAction.title"></message>
+            </button>
+        </div>
+    </div>
+
+    <!-- RESULTS -->
+
+    <div id="search-results-container">
+
+ <div class="alert-container" ng-show="criteria.userNicknameNotFound"> + <strong><message key="listAuthorizationPkgRules.userNicknameNotFound.title"></message></strong> + <message key="listAuthorizationPkgRules.userNicknameNotFound.description"></message>
+        </div>
+
+        <div class="alert-container" ng-show="criteria.pkgNameNotFound">
+ <strong><message key="listAuthorizationPkgRules.pkgNameNotFound.title"></message></strong> + <message key="listAuthorizationPkgRules.pkgNameNotFound.description"></message>
+        </div>
+
+ <div ng-show="rules.items && 0==rules.items.length" class="info-container"> + <strong><message key="listAuthorizationPkgRules.noResults.title"></message>; </strong> + <message key="listAuthorizationPkgRules.noResults.description"></message>
+        </div>
+
+ <div ng-show="rules.items && rules.items.length" class="table-general-container">
+
+            <div class="table-general-pagination-container">
+                <pagination-control
+                        link-count="9"
+                        max="rules.max"
+                        total="rules.total"
+                        offset="rules.offset"></pagination-control>
+            </div>
+
+            <table class="table-general">
+                <thead>
+ <th><message key="listAuthorizationPkgRules.table.user.title"></message></th> + <th><message key="listAuthorizationPkgRules.table.permission.title"></message></th> + <th><message key="listAuthorizationPkgRules.table.pkg.title"></message></th>
+                <th></th>
+                </thead>
+                <tbody>
+                <tr ng-repeat="item in rules.items">
+                    <td><user-label user="item.user"></user-label></td>
+                    <td>{{item.permission.title}}</td>
+                    <td>
+ <span ng-show="item.pkg"><pkg-label pkg="item.pkg"></pkg-label></span>
+                        <span ng-show="!item.pkg">
+                            <em>
+ <message key="listAuthorizationPkgRules.allPackages"></message>
+                            </em>
+                        </span>
+                    </td>
+                    <td>
+                        <a href="" ng-click="goDeleteRule(item)">
+                            <img src="/img/rowdelete.svg">
+                        </a>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+
+        </div>
+    </div>
+
+    <ul>
+        <li>
+            <a href="" ng-click="goAddRule()">
+ <message key="listAuthorizationPkgRules.addRuleAction.title"></message>
+            </a>
+        </li>
+    </ul>
+
+ <modal-container show="!!ruleToDelete" width="320" height="210" close="goCancelDeleteRule()">
+
+ <h3><message key="listAuthorizationPkgRules.deleteRuleModal.title"></message></h3> + <p><message key="listAuthorizationPkgRules.deleteRuleModal.description"></message></p>
+
+        <div>
+            <button ng-click="goConfirmDeleteRule()">
+ <message key="listAuthorizationPkgRules.deleteRuleModal.confirmAction.title"></message>
+            </button>
+            <button ng-click="goCancelDeleteRule()">
+ <message key="listAuthorizationPkgRules.deleteRuleModal.cancelAction.title"></message>
+            </button>
+        </div>
+
+    </modal-container>
+
+</div>
+
+<div class="footer"></div>
+<spinner spin="shouldSpin()"></spinner>
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrulescontroller.js Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+angular.module('haikudepotserver').controller(
+    'ListAuthorizationPkgRulesController',
+    [
+        '$scope','$log','$location',
+        
'jsonRpc','constants','errorHandling','userState','breadcrumbs','breadcrumbFactory',
+        'messageSource',
+        function(
+            $scope,$log,$location,
+ jsonRpc,constants,errorHandling,userState,breadcrumbs,breadcrumbFactory,
+            messageSource) {
+
+            var PAGESIZE = 15;
+
+            // used for the search.
+            var KEY_OFFSET = 'o';
+            var KEY_USERNICKNAME = 'un';
+            var KEY_PKGNAME = 'pn';
+
+            var amFetchingRules = false;
+
+ // this gets set to the rule that should be deleted and a modal will appear
+            // based off that.
+
+            $scope.ruleToDelete = undefined;
+
+            $scope.rules = {
+                items: undefined,
+ offset: $location.search()[KEY_OFFSET] ? parseInt($location.search()[KEY_OFFSET],10) : 0,
+                max: PAGESIZE,
+                total: undefined
+            };
+
+            $scope.criteria = {
+ userNickname : $location.search()[KEY_USERNICKNAME] ? $location.search()[KEY_USERNICKNAME] : '',
+                userNicknameNotFound : false,
+ pkgName : $location.search()[KEY_PKGNAME] ? $location.search()[KEY_PKGNAME] : '',
+                pkgNameNotFound : false
+            };
+
+            function resetNotFounds() {
+                $scope.criteria.userNicknameNotFound = false;
+                $scope.criteria.pkgNameNotFound = false;
+            }
+
+            $scope.userNicknameDidChange = function() {
+                resetNotFounds();
+            };
+
+            $scope.pkgNameDidChange = function() {
+                resetNotFounds();
+            };
+
+            $scope.shouldSpin = function() {
+                return amFetchingRules;
+            };
+
+            function refetchRules() {
+
+                amFetchingRules = true;
+
+ // copy all of the search specifications into the search parameters of the route.
+
+ $location.search(KEY_OFFSET, $scope.rules.offset ? ''+$scope.rules.offset : '0'); + $location.search(KEY_PKGNAME, $scope.criteria.pkgName ? $scope.criteria.pkgName : ''); + $location.search(KEY_USERNICKNAME, $scope.criteria.userNickname ? $scope.criteria.userNickname : '');
+                breadcrumbs.peek().search = $location.search();
+
+                jsonRpc.call(
+                    constants.ENDPOINT_API_V1_AUTHORIZATION,
+                    "searchAuthorizationPkgRules",
+                    [{
+                        userNickname : $scope.criteria.userNickname,
+                        pkgName : $scope.criteria.pkgName,
+                        offset : $scope.rules.offset,
+                        limit : $scope.rules.max
+                    }]
+                ).then(
+                    function(result) {
+
+                        $scope.rules.items = _.map(
+                            result.items,
+                            function(item) {
+                                return {
+ user : { nickname : item.userNickname }, + permission : { code : item.permissionCode, title : '...' }, + pkg : item.pkgName ? { name : item.pkgName } : undefined
+                                };
+                            }
+                        );
+
+                        _.each(
+                            $scope.rules.items,
+                            function(item) {
+ messageSource.get(userState.naturalLanguageCode(),'permission.' + item.permission.code + '.title').then(
+                                    function(value) {
+                                        item.permission.title = value;
+                                    },
+                                    function() {
+                                        result.title = '???';
+                                    }
+                                );
+                            }
+                        );
+
+                        $scope.rules.total = result.total;
+                        $log.info('found '+result.items.length+' rules');
+                        amFetchingRules = false;
+
+                    },
+                    function(err) {
+
+                        $scope.rules.items = [];
+                        $scope.rules.total = 0;
+
+                        switch(err.code) {
+
+                            case jsonRpc.errorCodes.OBJECTNOTFOUND:
+
+                                switch(err.data.entityName) {
+
+                                    case 'User':
+ $scope.criteria.userNicknameNotFound = true;
+                                        break;
+
+                                    case 'Pkg':
+ $scope.criteria.pkgNameNotFound = true;
+                                        break;
+
+                                    default:
+ errorHandling.handleJsonRpcError(err);
+                                        break;
+
+                                }
+
+                                break;
+
+                            default:
+                                errorHandling.handleJsonRpcError(err);
+                                break;
+
+                        }
+
+                        amFetchingRules = false;
+
+                    }
+                );
+
+            };
+
+            $scope.goSearch = function() {
+                $scope.rules.offset = 0;
+                refetchRules();
+            }
+
+            $scope.goAddRule = function() {
+ breadcrumbs.pushAndNavigate(breadcrumbFactory.createAddAuthorizationPkgRule());
+            };
+
+            $scope.goCancelDeleteRule = function() {
+                $scope.ruleToDelete = undefined;
+            };
+
+            $scope.goConfirmDeleteRule = function() {
+
+                jsonRpc.call(
+                    constants.ENDPOINT_API_V1_AUTHORIZATION,
+                    "removeAuthorizationPkgRule",
+                    [{
+                        userNickname : $scope.ruleToDelete.user.nickname,
+ pkgName : $scope.ruleToDelete.pkg ? $scope.ruleToDelete.pkg.name : null, + permissionCode : $scope.ruleToDelete.permission.code
+                    }]
+                ).then(
+                    function() {
+                        $scope.ruleToDelete = undefined;
+                        $log.info('did delete an authorization pkg rule');
+                        $scope.goSearch();
+                    },
+                    function(err) {
+                        errorHandling.handleJsonRpcError(err);
+                    }
+                );
+
+            };
+
+            /**
+ * <p>Hit when somebody clicks on the cross at the end of the row relating to a rule.</p>
+             */
+
+            $scope.goDeleteRule = function(rule) {
+                $scope.ruleToDelete = rule;
+            };
+
+            // --------------------
+            // WATCHES + EVENTS
+
+            $scope.$watch('rules.offset', function() {
+                refetchRules();
+            });
+
+        }
+    ]
+);
=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java Thu Jul 24 11:35:00 2014 UTC
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.api1;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import junit.framework.Assert;
+import org.apache.cayenne.ObjectContext;
+import org.fest.assertions.Assertions;
+import org.haikuos.haikudepotserver.AbstractIntegrationTest;
+import org.haikuos.haikudepotserver.IntegrationTestSupportService;
+import org.haikuos.haikudepotserver.api1.model.authorization.*;
+import org.haikuos.haikudepotserver.api1.support.AuthorizationFailureException;
+import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
+import org.haikuos.haikudepotserver.dataobjects.PermissionUserPkg;
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.model.Permission;
+import org.junit.Test;
+
+import javax.annotation.Resource;
+
+public class AuthorizationApiIT extends AbstractIntegrationTest {
+
+    @Resource
+    AuthorizationApi authorizationApi;
+
+    private void assertTargetAndPermission(
+            IntegrationTestSupportService.StandardTestData data,
+ CheckAuthorizationResult.AuthorizationTargetAndPermission targetAndPermission,
+            boolean result) {
+ Assertions.assertThat(targetAndPermission.permissionCode).isEqualTo(Permission.PKG_EDITICON.name()); + Assertions.assertThat(targetAndPermission.targetIdentifier).isEqualTo(data.pkg1.getName()); + Assertions.assertThat(targetAndPermission.targetType).isEqualTo(AuthorizationTargetType.PKG); + Assertions.assertThat(targetAndPermission.authorized).isEqualTo(result);
+    }
+
+    @Test
+    public void checkAuthorizationRequest_asUnauthenticated() {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                data.pkg1.getName(),
+                Permission.PKG_EDITICON.name()));
+
+        // ------------------------------------
+ CheckAuthorizationResult result = authorizationApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + assertTargetAndPermission(data, result.targetAndPermissions.get(0), false);
+
+    }
+
+ // TODO : when some more sophisticated cases are available; implement some better tests
+    @Test
+    public void checkAuthorizationRequest_asRoot() {
+ IntegrationTestSupportService.StandardTestData data = integrationTestSupportService.createStandardTestData();
+
+        setAuthenticatedUserToRoot();
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                data.pkg1.getName(),
+                Permission.PKG_EDITICON.name()));
+
+        // ------------------------------------
+ CheckAuthorizationResult result = authorizationApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + assertTargetAndPermission(data, result.targetAndPermissions.get(0), true);
+
+    }
+
+    /**
+ * <p>This test checks to see what happens if you try to create a new rule, but you are not
+     * authorized to do so.</p>
+     */
+
+    @Test
+ public void testCreateAuthorizationRule_unauthorized() throws ObjectNotFoundException, AuthorizationRuleConflictException {
+        integrationTestSupportService.createStandardTestData();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ integrationTestSupportService.createBasicUser(context,"testuser","fakepassword");
+        }
+
+        setAuthenticatedUser("testuser");
+
+ CreateAuthorizationPkgRuleRequest request = new CreateAuthorizationPkgRuleRequest();
+        request.userNickname = "testuser";
+ request.permissionCode = Permission.PKG_EDITICON.name().toLowerCase();
+        request.pkgName = "pkg1";
+
+        try {
+
+            // ------------------------------------
+            authorizationApi.createAuthorizationPkgRule(request);
+            // ------------------------------------
+
+            Assert.fail("expected authorization to fail");
+        }
+        catch(AuthorizationFailureException afe) {
+            // expected
+        }
+
+    }
+
+    @Test
+ public void testCreateAuthorizationRule_permissionUserPkg() throws ObjectNotFoundException, AuthorizationRuleConflictException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ integrationTestSupportService.createBasicUser(context,"testuser","fakepassword");
+        }
+
+ CreateAuthorizationPkgRuleRequest request = new CreateAuthorizationPkgRuleRequest(); + request.permissionCode = Permission.PKG_EDITSCREENSHOT.name().toLowerCase();
+        request.pkgName = "pkg1";
+        request.userNickname = "testuser";
+
+        // ------------------------------------
+        authorizationApi.createAuthorizationPkgRule(request);
+        // ------------------------------------
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+            Optional<PermissionUserPkg> permissionUserPkgOptional =
+                    PermissionUserPkg.getByPermissionUserAndPkg(
+                            context,
+ org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, Permission.PKG_EDITSCREENSHOT.name().toLowerCase()).get(),
+                            User.getByNickname(context, "testuser").get(),
+                            Pkg.getByName(context, "pkg1").get());
+
+ Assertions.assertThat(permissionUserPkgOptional.isPresent()).isTrue();
+        }
+    }
+
+    @Test
+ public void testCreateAuthorizationRule_conflicting() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ integrationTestSupportService.createBasicUser(context,"testuser","fakepassword");
+        }
+
+ CreateAuthorizationPkgRuleRequest request = new CreateAuthorizationPkgRuleRequest(); + request.permissionCode = Permission.PKG_EDITSCREENSHOT.name().toLowerCase();
+        request.pkgName = "pkg1";
+        request.userNickname = "testuser";
+
+        try {
+            authorizationApi.createAuthorizationPkgRule(request);
+        }
+        catch(AuthorizationRuleConflictException e) {
+ throw new RuntimeException("was not expecting the first authorization rule creation to fail",e);
+        }
+
+        try {
+
+            // ------------------------------------
+            authorizationApi.createAuthorizationPkgRule(request);
+            // ------------------------------------
+
+ Assert.fail("expected an " + AuthorizationRuleConflictException.class.getSimpleName());
+
+        }
+        catch(AuthorizationRuleConflictException e) {
+            // expected
+        }
+        catch(Throwable th) {
+ Assert.fail("expected an " + AuthorizationRuleConflictException.class.getSimpleName() + ", but got an instance of " + th.getClass().getSimpleName());
+        }
+
+    }
+
+    /**
+ * <p>Checks to make sure that any permission that is assigned is only for packages as the target type. Other
+     * sorts of targets are not allowed to be used.</p>
+     */
+
+    @Test
+ public void testCreateAuthorizationRule_badPermission() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+ CreateAuthorizationPkgRuleRequest request = new CreateAuthorizationPkgRuleRequest(); + request.permissionCode = Permission.REPOSITORY_VIEW.name().toLowerCase();
+        request.pkgName = "pkg1";
+        request.userNickname = "testuser";
+
+        try {
+            // ------------------------------------
+            authorizationApi.createAuthorizationPkgRule(request);
+            // ------------------------------------
+
+ Assert.fail("expected an "+IllegalStateException.class.getSimpleName()+" to be thrown.");
+        }
+        catch(IllegalStateException ise) {
+            // expected
+        }
+        catch(Throwable th) {
+ Assert.fail("expected an "+IllegalStateException.class.getSimpleName()+", but got an "+th.getClass().getSimpleName()+" thrown instead");
+        }
+    }
+
+    @Test
+ public void testRemoveAuthorizationRule_permissionUserPkg() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ User user = integrationTestSupportService.createBasicUser(context, "testuser", "fakepassword"); + PermissionUserPkg permissionUserPkg = context.newObject(PermissionUserPkg.class); + permissionUserPkg.setPermission(org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, Permission.PKG_EDITICON.name().toLowerCase()).get());
+            permissionUserPkg.setUser(user);
+            permissionUserPkg.setPkg(Pkg.getByName(context, "pkg1").get());
+            context.commitChanges();
+        }
+
+ RemoveAuthorizationPkgRuleRequest request = new RemoveAuthorizationPkgRuleRequest();
+        request.userNickname = "testuser";
+ request.permissionCode = Permission.PKG_EDITICON.name().toLowerCase();
+        request.pkgName = "pkg1";
+
+        // ------------------------------------
+        authorizationApi.removeAuthorizationPkgRule(request);
+        // ------------------------------------
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+            Assertions.assertThat(
+                    PermissionUserPkg.getByPermissionUserAndPkg(
+                            context,
+ org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, Permission.PKG_EDITICON.name().toLowerCase()).get(),
+                            User.getByNickname(context, "testuser").get(),
+ Pkg.getByName(context, "pkg1").get()).isPresent()).isFalse();
+        }
+
+    }
+
+    private void createSearchAuthorizationRuleTestData() {
+        ObjectContext context = serverRuntime.getContext();
+
+ User user1 = integrationTestSupportService.createBasicUser(context, "testuser1", "fakepassword"); + User user2 = integrationTestSupportService.createBasicUser(context, "testuser2", "fakepassword"); + User user3 = integrationTestSupportService.createBasicUser(context, "testuser3", "fakepassword");
+
+        Pkg pkg1 = Pkg.getByName(context, "pkg1").get();
+        Pkg pkg2 = Pkg.getByName(context, "pkg2").get();
+
+        org.haikuos.haikudepotserver.dataobjects.Permission permission =
+ org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, Permission.PKG_EDITICON.name().toLowerCase()).get();
+
+ PermissionUserPkg pup_u1p1 = context.newObject(PermissionUserPkg.class);
+        pup_u1p1.setPkg(pkg1);
+        pup_u1p1.setUser(user1);
+        pup_u1p1.setPermission(permission);
+
+ PermissionUserPkg pup_u2p1 = context.newObject(PermissionUserPkg.class);
+        pup_u2p1.setPkg(pkg1);
+        pup_u2p1.setUser(user2);
+        pup_u2p1.setPermission(permission);
+
+ PermissionUserPkg pup_u3p1 = context.newObject(PermissionUserPkg.class);
+        pup_u3p1.setPkg(pkg1);
+        pup_u3p1.setUser(user3);
+        pup_u3p1.setPermission(permission);
+
+ PermissionUserPkg pup_u2p2 = context.newObject(PermissionUserPkg.class);
+        pup_u2p2.setPkg(pkg2);
+        pup_u2p2.setUser(user2);
+        pup_u2p2.setPermission(permission);
+
+        context.commitChanges();
+    }
+
+    @Test
+ public void testSearchAuthorizationRule_pkgWithUser() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+        createSearchAuthorizationRuleTestData();
+
+ SearchAuthorizationPkgRulesRequest request = new SearchAuthorizationPkgRulesRequest();
+        request.userNickname = "testuser2";
+        request.pkgName = null;
+        request.offset = 0;
+        request.limit = 10;
+
+        // ------------------------------------
+ SearchAuthorizationPkgRulesResult result = authorizationApi.searchAuthorizationPkgRules(request);
+        // ------------------------------------
+
+        {
+            Assertions.assertThat(result.total).isEqualTo(2);
+            Assertions.assertThat(result.items.size()).isEqualTo(2);
+
+            Assertions.assertThat(Sets.newHashSet(Lists.transform(
+                    result.items,
+ new Function<SearchAuthorizationPkgRulesResult.Rule, String>() {
+                        @Override
+ public String apply(SearchAuthorizationPkgRulesResult.Rule input) {
+                            return input.pkgName;
+                        }
+                    }
+            ))).isEqualTo(ImmutableSet.of("pkg1", "pkg2"));
+        }
+
+    }
+
+    @Test
+ public void testSearchAuthorizationRule_pkg() throws ObjectNotFoundException {
+        integrationTestSupportService.createStandardTestData();
+        setAuthenticatedUserToRoot();
+        createSearchAuthorizationRuleTestData();
+
+ SearchAuthorizationPkgRulesRequest request = new SearchAuthorizationPkgRulesRequest();
+        request.userNickname = null;
+        request.pkgName = "pkg1";
+        request.offset = 0;
+        request.limit = 10;
+
+        // ------------------------------------
+ SearchAuthorizationPkgRulesResult result = authorizationApi.searchAuthorizationPkgRules(request);
+        // ------------------------------------
+
+        {
+            Assertions.assertThat(result.total).isEqualTo(3);
+            Assertions.assertThat(result.items.size()).isEqualTo(3);
+
+            Assertions.assertThat(Sets.newHashSet(Lists.transform(
+                    result.items,
+ new Function<SearchAuthorizationPkgRulesResult.Rule, String>() {
+                        @Override
+ public String apply(SearchAuthorizationPkgRulesResult.Rule input) {
+                            return input.userNickname;
+                        }
+                    }
+ ))).isEqualTo(ImmutableSet.of("testuser1", "testuser2", "testuser3"));
+        }
+
+    }
+
+}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/AuthorizationTargetType.java Sat May 24 11:47:09 2014 UTC
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotserver.api1.model;
-
-/**
- * <p>This enum is used to identify what type of target the authorization applies to.</p>
- */
-
-public enum AuthorizationTargetType {
-    PKG,
-    USER,
-    REPOSITORY,
-    USERRATING
-}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/CheckAuthorizationRequest.java Sat Feb 8 09:19:06 2014 UTC
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotserver.api1.model.miscellaneous;
-
-import org.haikuos.haikudepotserver.api1.model.AuthorizationTargetType;
-
-import java.util.List;
-
-public class CheckAuthorizationRequest {
-
-    public List<AuthorizationTargetAndPermission> targetAndPermissions;
-
-    public static class AuthorizationTargetAndPermission {
-
-        /**
- * <p>The target type defines what sort of object you want to check your authorization for. The #targetIdentifier
-         * then identifies an instance of that type.</p>
-         */
-
-        public AuthorizationTargetType targetType;
-
-        /**
- * This identifier will identify an instance of the #targetType that has the authorization applied to it. Some - * permissions may not require a target identifier; in which case this value can be supplied as null.
-         */
-
-        public String targetIdentifier;
-
-        /**
- * <p>This is a list of permissions that the client would like to check for in the context of the target
-         * identified by other parameters in this request.</p>
-         */
-
-        public String permissionCode;
-
-        public AuthorizationTargetAndPermission() {
-        }
-
- public AuthorizationTargetAndPermission(AuthorizationTargetType targetType, String targetIdentifier, String permissionCode) {
-            this.targetType = targetType;
-            this.targetIdentifier = targetIdentifier;
-            this.permissionCode = permissionCode;
-        }
-    }
-
-}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/model/miscellaneous/CheckAuthorizationResult.java Sat Feb 8 09:19:06 2014 UTC
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotserver.api1.model.miscellaneous;
-
-import org.haikuos.haikudepotserver.api1.model.AuthorizationTargetType;
-
-import java.util.List;
-
-public class CheckAuthorizationResult {
-
-    public List<AuthorizationTargetAndPermission> targetAndPermissions;
-
-    public static class AuthorizationTargetAndPermission {
-
-        /**
- * <p>The target type defines what sort of object you want to check your authorization for. The #targetIdentifier
-         * then identifies an instance of that type.</p>
-         */
-
-        public AuthorizationTargetType targetType;
-
-        /**
- * This identifier will identify an instance of the #targetType that has the authorization applied to it. Some - * permissions may not require a target identifier; in which case this value can be supplied as null.
-         */
-
-        public String targetIdentifier;
-
-        /**
- * <p>This is a list of permissions that the client would like to check for in the context of the target
-         * identified by other parameters in this request.</p>
-         */
-
-        public String permissionCode;
-
-        /**
- * <p>This boolean will be true if the target is authorized; false if not.</p>
-         */
-
-        public Boolean authorized;
-    }
-
-
-}
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionPublicRepository.java Sun Jun 29 11:22:04 2014 UTC
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotserver.dataobjects;
-
-import org.haikuos.haikudepotserver.dataobjects.auto._PermissionPublicRepository;
-
-public class PermissionPublicRepository extends _PermissionPublicRepository {
-}
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionUserRepository.java Sun Jun 29 11:22:04 2014 UTC
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- * Copyright 2014, Andrew Lindesay
- * Distributed under the terms of the MIT License.
- */
-
-package org.haikuos.haikudepotserver.dataobjects;
-
-import org.haikuos.haikudepotserver.dataobjects.auto._PermissionUserRepository;
-
-public class PermissionUserRepository extends _PermissionUserRepository {
-
-}
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionPublicRepository.java Sun Jun 29 11:22:04 2014 UTC
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.haikuos.haikudepotserver.dataobjects.auto;
-
-import org.haikuos.haikudepotserver.dataobjects.Permission;
-import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject;
-
-/**
- * Class _PermissionPublicRepository was generated by Cayenne.
- * It is probably a good idea to avoid changing this class manually,
- * since it may be overwritten next time code is regenerated.
- * If you need to make any customizations, please use subclass.
- */
-public abstract class _PermissionPublicRepository extends AbstractDataObject {
-
-    public static final String REQUIRES_USER_PROPERTY = "requiresUser";
-    public static final String PERMISSION_PROPERTY = "permission";
-    public static final String REPOSITORY_PROPERTY = "repository";
-
-    public static final String ID_PK_COLUMN = "id";
-
-    public void setRequiresUser(Boolean requiresUser) {
-        writeProperty(REQUIRES_USER_PROPERTY, requiresUser);
-    }
-    public Boolean getRequiresUser() {
-        return (Boolean)readProperty(REQUIRES_USER_PROPERTY);
-    }
-
-    public void setPermission(Permission permission) {
-        setToOneTarget(PERMISSION_PROPERTY, permission, true);
-    }
-
-    public Permission getPermission() {
-        return (Permission)readProperty(PERMISSION_PROPERTY);
-    }
-
-
-    public void setRepository(Repository repository) {
-        setToOneTarget(REPOSITORY_PROPERTY, repository, true);
-    }
-
-    public Repository getRepository() {
-        return (Repository)readProperty(REPOSITORY_PROPERTY);
-    }
-
-
-}
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionUserRepository.java Sun Jun 29 11:22:04 2014 UTC
+++ /dev/null
@@ -1,49 +0,0 @@
-package org.haikuos.haikudepotserver.dataobjects.auto;
-
-import org.haikuos.haikudepotserver.dataobjects.Permission;
-import org.haikuos.haikudepotserver.dataobjects.Repository;
-import org.haikuos.haikudepotserver.dataobjects.User;
-import org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject;
-
-/**
- * Class _PermissionUserRepository was generated by Cayenne.
- * It is probably a good idea to avoid changing this class manually,
- * since it may be overwritten next time code is regenerated.
- * If you need to make any customizations, please use subclass.
- */
-public abstract class _PermissionUserRepository extends AbstractDataObject {
-
-    public static final String PERMISSION_PROPERTY = "permission";
-    public static final String REPOSITORY_PROPERTY = "repository";
-    public static final String USER_PROPERTY = "user";
-
-    public static final String ID_PK_COLUMN = "id";
-
-    public void setPermission(Permission permission) {
-        setToOneTarget(PERMISSION_PROPERTY, permission, true);
-    }
-
-    public Permission getPermission() {
-        return (Permission)readProperty(PERMISSION_PROPERTY);
-    }
-
-
-    public void setRepository(Repository repository) {
-        setToOneTarget(REPOSITORY_PROPERTY, repository, true);
-    }
-
-    public Repository getRepository() {
-        return (Repository)readProperty(REPOSITORY_PROPERTY);
-    }
-
-
-    public void setUser(User user) {
-        setToOneTarget(USER_PROPERTY, user, true);
-    }
-
-    public User getUser() {
-        return (User)readProperty(USER_PROPERTY);
-    }
-
-
-}
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java Mon Apr 21 10:48:33 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApi.java Thu Jul 24 11:35:00 2014 UTC
@@ -24,13 +24,6 @@

GetAllNaturalLanguagesResult getAllNaturalLanguages(GetAllNaturalLanguagesRequest getAllNaturalLanguagesRequest);

-    /**
- * <p>This method will take in a list of permissions with targets and will return the list of those that - * pass authorization checks against the presently authenticated user.</p>
-     */
-
- CheckAuthorizationResult checkAuthorization(CheckAuthorizationRequest deriveAuthorizationRequest);
-
     /**
* <p>This method will raise a runtime exception to test the behaviour of the server and client in this
      * situation.</p>
=======================================
--- /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java Wed Apr 9 11:01:12 2014 UTC +++ /haikudepotserver-api1/src/main/java/org/haikuos/haikudepotserver/api1/support/Constants.java Thu Jul 24 11:35:00 2014 UTC
@@ -13,5 +13,6 @@
     public final static int ERROR_CODE_AUTHORIZATIONFAILURE = -32803;
     public final static int ERROR_CODE_BADPKGICON = -32804;
     public final static int ERROR_CODE_LIMITEXCEEDED = -32805;
+    public final static int ERROR_CODE_AUTHORIZATIONRULECONFLICT = -32806;

 }
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf Thu Jul 24 11:35:00 2014 UTC
Binary file, no diff available.
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-config.tex Sat Jul 12 08:36:41 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-config.tex Thu Jul 24 11:35:00 2014 UTC
@@ -63,6 +63,12 @@
 \subsubsection{\tt jawr.debug.on}

This configuration relates to the \href{https://jawr.java.net/}{JAWR} debug setting. It will default to ``false'.
+
+\subsubsection{\tt passwordreset.baseurl}
+
+This is the base URL to which password-reset requests will be directed. The URL base is included in the email body to users who have requested a password-reset. This has to be configured because the application itself does not know the path by which the HTTP request had reached it. In the case of a development environment, this base URL might be something like;
+
+\framebox{http://localhost:8080/passwordreset/}

 \subsection{Token Bearer Authentication}

@@ -111,3 +117,7 @@
 \subsubsection{\tt smtp.starttls}

Possible values are ``true'' and ``false''. Configures the SMTP transport. This value is optional and defaults to ``false''.
+
+\subsubsection{\tt email.from}
+
+This is the email address from which emails outbound from the system will be sent. Typically this might be a ``no-reply'' email address such as ``noreply@xxxxxxxxxxxx''.
=======================================
--- /haikudepotserver-parent/pom.xml    Sat Jul 12 08:36:41 2014 UTC
+++ /haikudepotserver-parent/pom.xml    Thu Jul 24 11:35:00 2014 UTC
@@ -18,7 +18,7 @@
 within the jars themselves.
 -->

-        <web-angularjs.versionbase>1.2.18</web-angularjs.versionbase>
+        <web-angularjs.versionbase>1.2.20</web-angularjs.versionbase>
         <web-angularjs.versionextension></web-angularjs.versionextension>

         <web-underscorejs.versionbase>1.6.0</web-underscorejs.versionbase>
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Tue Jul 8 09:00:54 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/MiscellaneousApiImpl.java Thu Jul 24 11:35:00 2014 UTC
@@ -14,9 +14,6 @@
 import org.haikuos.haikudepotserver.api1.model.miscellaneous.*;
 import org.haikuos.haikudepotserver.api1.support.ObjectNotFoundException;
 import org.haikuos.haikudepotserver.dataobjects.*;
-import org.haikuos.haikudepotserver.security.AuthorizationService;
-import org.haikuos.haikudepotserver.security.model.Permission;
-import org.haikuos.haikudepotserver.security.model.TargetType;
 import org.haikuos.haikudepotserver.support.Closeables;
 import org.haikuos.haikudepotserver.support.RuntimeInformationService;
 import org.slf4j.Logger;
@@ -41,9 +38,6 @@
     @Resource
     ServerRuntime serverRuntime;

-    @Resource
-    AuthorizationService authorizationService;
-
     @Resource
     RuntimeInformationService runtimeInformationService;

@@ -86,37 +80,6 @@
                 )
         );
     }
-
-    @Override
- public CheckAuthorizationResult checkAuthorization(CheckAuthorizationRequest deriveAuthorizationRequest) {
-
-        Preconditions.checkNotNull(deriveAuthorizationRequest);
- Preconditions.checkNotNull(deriveAuthorizationRequest.targetAndPermissions);
-
-        final ObjectContext context = serverRuntime.getContext();
-        CheckAuthorizationResult result = new CheckAuthorizationResult();
-        result.targetAndPermissions = Lists.newArrayList();
-
- for(CheckAuthorizationRequest.AuthorizationTargetAndPermission targetAndPermission : deriveAuthorizationRequest.targetAndPermissions) {
-
- CheckAuthorizationResult.AuthorizationTargetAndPermission authorizationTargetAndPermission = new CheckAuthorizationResult.AuthorizationTargetAndPermission();
-
- authorizationTargetAndPermission.permissionCode = targetAndPermission.permissionCode; - authorizationTargetAndPermission.targetIdentifier = targetAndPermission.targetIdentifier; - authorizationTargetAndPermission.targetType = targetAndPermission.targetType;
-
- authorizationTargetAndPermission.authorized = authorizationService.check(
-                    context,
-                    tryObtainAuthenticatedUser(context).orNull(),
- null!=targetAndPermission.targetType ? TargetType.valueOf(targetAndPermission.targetType.name()) : null,
-                    targetAndPermission.targetIdentifier,
- Permission.valueOf(targetAndPermission.permissionCode));
-
- result.targetAndPermissions.add(authorizationTargetAndPermission);
-        }
-
-        return result;
-    }

     @Override
public RaiseExceptionResult raiseException(RaiseExceptionRequest raiseExceptionRequest) {
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java Wed Apr 9 11:01:12 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/api1/support/ErrorResolverImpl.java Thu Jul 24 11:35:00 2014 UTC
@@ -14,6 +14,7 @@
 import com.googlecode.jsonrpc4j.ErrorResolver;
 import org.apache.cayenne.validation.BeanValidationFailure;
 import org.apache.cayenne.validation.SimpleValidationFailure;
+import org.haikuos.haikudepotserver.api1.model.authorization.AuthorizationRuleConflictException;

 import java.lang.reflect.Method;
 import java.util.List;
@@ -29,6 +30,13 @@
     @Override
public JsonError resolveError(Throwable t, Method method, List<JsonNode> arguments) {

+ if(AuthorizationRuleConflictException.class.isAssignableFrom(t.getClass())) {
+            return new JsonError(
+                    Constants.ERROR_CODE_AUTHORIZATIONRULECONFLICT,
+                    "authorizationruleconflict",
+                    null);
+        }
+
         if(LimitExceededException.class.isAssignableFrom(t.getClass())) {
             return new JsonError(
                     Constants.ERROR_CODE_LIMITEXCEEDED,
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Permission.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Permission.java Thu Jul 24 11:35:00 2014 UTC
@@ -5,8 +5,28 @@

 package org.haikuos.haikudepotserver.dataobjects;

+import com.google.common.base.Optional;
+import com.google.common.collect.Iterables;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.query.SelectQuery;
 import org.haikuos.haikudepotserver.dataobjects.auto._Permission;

+import java.util.List;
+
 public class Permission extends _Permission {

+ public static Optional<Permission> getByCode(ObjectContext context, String code) {
+        return Optional.fromNullable(Iterables.getOnlyElement(
+                (List<Permission>) context.performQuery(new SelectQuery(
+                        Permission.class,
+ ExpressionFactory.matchExp(Permission.CODE_PROPERTY, code))),
+                null));
+    }
+
+    @Override
+    public String toString() {
+        return "permission;"+getCode();
+    }
+
 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionUserPkg.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PermissionUserPkg.java Thu Jul 24 11:35:00 2014 UTC
@@ -5,8 +5,48 @@

 package org.haikuos.haikudepotserver.dataobjects;

+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.exp.ExpressionFactory;
+import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.validation.ValidationResult;
 import org.haikuos.haikudepotserver.dataobjects.auto._PermissionUserPkg;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;

-public class PermissionUserPkg extends _PermissionUserPkg {
+import java.util.List;
+
+public class PermissionUserPkg extends _PermissionUserPkg implements AuthorizationPkgRule {
+
+    public static Optional<PermissionUserPkg> getByPermissionUserAndPkg(
+            ObjectContext context,
+            Permission permission,
+            User user,
+            Pkg pkg) {
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(permission);
+        Preconditions.checkNotNull(user);
+        return Optional.fromNullable(Iterables.getOnlyElement(
+ (List<PermissionUserPkg>) context.performQuery(new SelectQuery(
+                                PermissionUserPkg.class,
+ ExpressionFactory.matchExp(PermissionUserPkg.PERMISSION_PROPERTY, permission).andExp( + ExpressionFactory.matchExp(PermissionUserPkg.USER_PROPERTY, user).andExp( + ExpressionFactory.matchExp(PermissionUserPkg.PKG_PROPERTY, pkg)
+                                        )
+                                ))),
+                        null)
+        );
+    }
+
+    @Override
+    public void validateForInsert(ValidationResult validationResult) {
+
+        if (null == getCreateTimestamp()) {
+            setCreateTimestamp(new java.util.Date());
+        }
+
+        super.validateForInsert(validationResult);
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Repository.java Sun Feb 9 10:38:52 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/Repository.java Thu Jul 24 11:35:00 2014 UTC
@@ -56,5 +56,10 @@
         }

     }
+
+    @Override
+    public String toString() {
+      return "repo;"+getCode();
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java Tue Jul 8 09:00:54 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java Thu Jul 24 11:35:00 2014 UTC
@@ -28,7 +28,7 @@

 public class User extends _User implements CreateAndModifyTimestamped {

- public final static Pattern NICKNAME_PATTERN = Pattern.compile("^[\\w]{4,16}$"); + public final static Pattern NICKNAME_PATTERN = Pattern.compile("^[a-z0-9]{4,16}$"); public final static Pattern PASSWORDHASH_PATTERN = Pattern.compile("^[a-f0-9]{64}$"); public final static Pattern PASSWORDSALT_PATTERN = Pattern.compile("^[a-f0-9]{64}$");

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Permission.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Permission.java Thu Jul 24 11:35:00 2014 UTC
@@ -11,11 +11,16 @@
 public abstract class _Permission extends AbstractDataObject {

     public static final String CODE_PROPERTY = "code";
+    public static final String NAME_PROPERTY = "name";

     public static final String ID_PK_COLUMN = "id";

     public String getCode() {
         return (String)readProperty(CODE_PROPERTY);
     }
+
+    public String getName() {
+        return (String)readProperty(NAME_PROPERTY);
+    }

 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionUserPkg.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PermissionUserPkg.java Thu Jul 24 11:35:00 2014 UTC
@@ -1,5 +1,7 @@
 package org.haikuos.haikudepotserver.dataobjects.auto;

+import java.util.Date;
+
 import org.haikuos.haikudepotserver.dataobjects.Permission;
 import org.haikuos.haikudepotserver.dataobjects.Pkg;
 import org.haikuos.haikudepotserver.dataobjects.User;
@@ -13,12 +15,20 @@
  */
 public abstract class _PermissionUserPkg extends AbstractDataObject {

+ public static final String CREATE_TIMESTAMP_PROPERTY = "createTimestamp";
     public static final String PERMISSION_PROPERTY = "permission";
     public static final String PKG_PROPERTY = "pkg";
     public static final String USER_PROPERTY = "user";

     public static final String ID_PK_COLUMN = "id";

+    public void setCreateTimestamp(Date createTimestamp) {
+        writeProperty(CREATE_TIMESTAMP_PROPERTY, createTimestamp);
+    }
+    public Date getCreateTimestamp() {
+        return (Date)readProperty(CREATE_TIMESTAMP_PROPERTY);
+    }
+
     public void setPermission(Permission permission) {
         setToOneTarget(PERMISSION_PROPERTY, permission, true);
     }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Repository.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_Repository.java Thu Jul 24 11:35:00 2014 UTC
@@ -1,11 +1,8 @@
 package org.haikuos.haikudepotserver.dataobjects.auto;

 import java.util.Date;
-import java.util.List;

 import org.haikuos.haikudepotserver.dataobjects.Architecture;
-import org.haikuos.haikudepotserver.dataobjects.PermissionPublicRepository;
-import org.haikuos.haikudepotserver.dataobjects.PermissionUserRepository;
 import org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject;

 /**
@@ -22,8 +19,6 @@
public static final String MODIFY_TIMESTAMP_PROPERTY = "modifyTimestamp";
     public static final String URL_PROPERTY = "url";
     public static final String ARCHITECTURE_PROPERTY = "architecture";
- public static final String PERMISSION_PUBLIC_REPOSITORIES_PROPERTY = "permissionPublicRepositories"; - public static final String PERMISSION_USER_REPOSITORIES_PROPERTY = "permissionUserRepositories";

     public static final String ID_PK_COLUMN = "id";

@@ -69,30 +64,6 @@
     public Architecture getArchitecture() {
         return (Architecture)readProperty(ARCHITECTURE_PROPERTY);
     }
-
-
- public void addToPermissionPublicRepositories(PermissionPublicRepository obj) { - addToManyTarget(PERMISSION_PUBLIC_REPOSITORIES_PROPERTY, obj, true);
-    }
- public void removeFromPermissionPublicRepositories(PermissionPublicRepository obj) { - removeToManyTarget(PERMISSION_PUBLIC_REPOSITORIES_PROPERTY, obj, true);
-    }
-    @SuppressWarnings("unchecked")
- public List<PermissionPublicRepository> getPermissionPublicRepositories() { - return (List<PermissionPublicRepository>)readProperty(PERMISSION_PUBLIC_REPOSITORIES_PROPERTY);
-    }
-
-
- public void addToPermissionUserRepositories(PermissionUserRepository obj) {
-        addToManyTarget(PERMISSION_USER_REPOSITORIES_PROPERTY, obj, true);
-    }
- public void removeFromPermissionUserRepositories(PermissionUserRepository obj) { - removeToManyTarget(PERMISSION_USER_REPOSITORIES_PROPERTY, obj, true);
-    }
-    @SuppressWarnings("unchecked")
-    public List<PermissionUserRepository> getPermissionUserRepositories() {
- return (List<PermissionUserRepository>)readProperty(PERMISSION_USER_REPOSITORIES_PROPERTY);
-    }


 }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_User.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_User.java Thu Jul 24 11:35:00 2014 UTC
@@ -5,7 +5,6 @@

 import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
 import org.haikuos.haikudepotserver.dataobjects.PermissionUserPkg;
-import org.haikuos.haikudepotserver.dataobjects.PermissionUserRepository;
 import org.haikuos.haikudepotserver.dataobjects.UserPasswordResetToken;
 import org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject;

@@ -28,7 +27,6 @@
     public static final String PASSWORD_SALT_PROPERTY = "passwordSalt";
public static final String NATURAL_LANGUAGE_PROPERTY = "naturalLanguage"; public static final String PERMISSION_USER_PKGS_PROPERTY = "permissionUserPkgs"; - public static final String PERMISSION_USER_REPOSITORIES_PROPERTY = "permissionUserRepositories"; public static final String USER_PASSWORD_RESET_TOKENS_PROPERTY = "userPasswordResetTokens";

     public static final String ID_PK_COLUMN = "id";
@@ -115,18 +113,6 @@
     public List<PermissionUserPkg> getPermissionUserPkgs() {
return (List<PermissionUserPkg>)readProperty(PERMISSION_USER_PKGS_PROPERTY);
     }
-
-
- public void addToPermissionUserRepositories(PermissionUserRepository obj) {
-        addToManyTarget(PERMISSION_USER_REPOSITORIES_PROPERTY, obj, true);
-    }
- public void removeFromPermissionUserRepositories(PermissionUserRepository obj) { - removeToManyTarget(PERMISSION_USER_REPOSITORIES_PROPERTY, obj, true);
-    }
-    @SuppressWarnings("unchecked")
-    public List<PermissionUserRepository> getPermissionUserRepositories() {
- return (List<PermissionUserRepository>)readProperty(PERMISSION_USER_REPOSITORIES_PROPERTY);
-    }


     public void addToUserPasswordResetTokens(UserPasswordResetToken obj) {
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/passwordreset/PasswordResetOrchestrationService.java Tue Jul 8 09:00:54 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/passwordreset/PasswordResetOrchestrationService.java Thu Jul 24 11:35:00 2014 UTC
@@ -254,13 +254,13 @@
                         UserPasswordResetToken.CREATE_TIMESTAMP_PROPERTY,
                         now.minusHours(timeToLiveHours).toDate()));

-        List<UserPasswordResetToken> tokens = context.performQuery(query);
+ List<UserPasswordResetToken> tokens = (List<UserPasswordResetToken>) context.performQuery(query);

         if(tokens.isEmpty()) {
-            logger.info("no expired tokens to delete");
+            logger.debug("no expired tokens to delete");
         }
         else {
- context.deleteObjects(tokens.toArray(new UserPasswordResetToken[] {})); + context.deleteObjects(tokens.toArray(new UserPasswordResetToken[tokens.size()]));
             context.commitChanges();
             logger.info("did delete {} expired tokens", tokens.size());
         }
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/PkgOrchestrationService.java Thu Jul 24 11:35:00 2014 UTC
@@ -322,6 +322,8 @@
         Preconditions.checkNotNull(context);
         Preconditions.checkNotNull(search);
         Preconditions.checkNotNull(search.getNaturalLanguage());
+        Preconditions.checkState(search.getOffset() >= 0);
+        Preconditions.checkState(search.getLimit() > 0);

         if(null!=search.getPkgNames() && search.getPkgNames().isEmpty()) {
             return Collections.emptyList();
@@ -734,7 +736,7 @@
                     pkg.getName());
         }

-        logger.info("have processed package {}",pkg.toString());
+        logger.debug("have processed package {}",pkg.toString());

     }

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Sat Jul 12 08:36:41 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/pkg/controller/PkgIconController.java Thu Jul 24 11:35:00 2014 UTC
@@ -111,7 +111,7 @@
         Optional<Pkg> pkg = Pkg.getByName(context, pkgName);

         if(!pkg.isPresent()) {
- logger.info("request for icon for package '{}', but no such package was able to be found",pkgName); + logger.debug("request for icon for package '{}', but no such package was able to be found",pkgName);
             throw new PkgNotFound();
         }

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java Wed Jun 4 11:36:29 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/repository/RepositoryImportService.java Thu Jul 24 11:35:00 2014 UTC
@@ -108,7 +108,7 @@
temporaryFile = File.createTempFile(job.getCode()+"__import",".hpkr"); Resources.asByteSource(url).copyTo(Files.asByteSink(temporaryFile));

- logger.info("did copy data for repository {} ({}) to temporary file",job.getCode(),url.toString()); + logger.debug("did copy data for repository {} ({}) to temporary file",job.getCode(),url.toString());

org.haikuos.pkg.HpkrFileExtractor fileExtractor = new org.haikuos.pkg.HpkrFileExtractor(temporaryFile); PkgIterator pkgIterator = new PkgIterator(fileExtractor.getPackageAttributesIterator());
=======================================
***Additional files exist in this changeset.***

==============================================================================
Revision: 4ab6d25e246d
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Jul 26 04:02:29 2014 UTC
Log:      + german localization for authorization rules management
+ checks (enforcement) on the authorization rules for packages

http://code.google.com/p/haiku-depot-web-app/source/detail?r=4ab6d25e246d

Modified:
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java
 /haikudepotserver-webapp/src/main/resources/messages.properties
 /haikudepotserver-webapp/src/main/resources/messages_de.properties

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/User.java Sat Jul 26 04:02:29 2014 UTC
@@ -7,7 +7,9 @@

 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.hash.Hashing;
 import org.apache.cayenne.ObjectContext;
@@ -19,6 +21,7 @@
 import org.apache.cayenne.validation.ValidationResult;
 import org.haikuos.haikudepotserver.dataobjects.auto._User;
import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;

 import javax.mail.internet.AddressException;
 import javax.mail.internet.InternetAddress;
@@ -135,6 +138,25 @@
         }

     }
+
+    /**
+ * <p>This method will return all of the rules pertaining to the supplied package; including those
+     * rules that might apply to any package.</p>
+     */
+
+ public List<? extends AuthorizationPkgRule> getAuthorizationPkgRules(final Pkg pkg) {
+        Preconditions.checkNotNull(pkg);
+
+        return ImmutableList.copyOf(Iterables.filter(
+                getPermissionUserPkgs(),
+                new Predicate<PermissionUserPkg>() {
+                    @Override
+                    public boolean apply(PermissionUserPkg input) {
+ return null==input.getPkg() || input.getPkg().equals(pkg);
+                    }
+                }
+        ));
+    }

     /**
      * <p>This method will configure a random salt value.</p>
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Sat Jul 26 04:02:29 2014 UTC
@@ -228,7 +228,7 @@

PermissionUserPkg rule = context.newObject(PermissionUserPkg.class);
         rule.setPermission(permission);
-        rule.setUser(user);
+ user.addToManyTarget(User.PERMISSION_USER_PKGS_PROPERTY, rule, true);
         rule.setPkg(pkg);
logger.info("did create permission user repository; {},{},{}",permission,user,pkg);

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationService.java Sat Jul 26 04:02:29 2014 UTC
@@ -7,17 +7,22 @@

 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
 import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
 import org.apache.cayenne.DataObject;
 import org.apache.cayenne.ObjectContext;
 import org.haikuos.haikudepotserver.dataobjects.Pkg;
 import org.haikuos.haikudepotserver.dataobjects.Repository;
 import org.haikuos.haikudepotserver.dataobjects.User;
 import org.haikuos.haikudepotserver.dataobjects.UserRating;
+import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;
 import org.haikuos.haikudepotserver.security.model.Permission;
 import org.haikuos.haikudepotserver.security.model.TargetType;
 import org.springframework.stereotype.Service;

+import java.util.List;
+
 /**
* <P>This class will provide functions around authorization. Some of the model for this is provided
  * by the API objects.</p>
@@ -48,6 +53,10 @@

throw new IllegalStateException("the data object type '"+dataObject.getClass().getSimpleName()+"' is not handled");
     }
+
+    /**
+ * <p>Returns true if the user supplied has the permission over the target object.</p>
+     */

     public boolean check(
             ObjectContext objectContext,
@@ -115,7 +124,7 @@
             ObjectContext objectContext,
             User authenticatedUser,
             DataObject target,
-            Permission permission) {
+            final Permission permission) {

         Preconditions.checkNotNull(permission);
         Preconditions.checkNotNull(objectContext);
@@ -127,6 +136,31 @@
         if(null!=authenticatedUser && !authenticatedUser.getActive()) {
throw new IllegalStateException("the authenticated user '"+authenticatedUser.getNickname()+"' is not active and so authorization queries cannot be resolved for them");
         }
+
+ // it could be that permission is afforded based on rules stored in the user. Check for
+        // this situation first.
+
+        if(null!=authenticatedUser) {
+            switch (permission) {
+                case PKG_EDITICON:
+                case PKG_EDITSCREENSHOT:
+                case PKG_EDITCATEGORIES:
+                case PKG_EDITVERSIONLOCALIZATION: {
+ List<? extends AuthorizationPkgRule> rules = authenticatedUser.getAuthorizationPkgRules((Pkg) target); + if (Iterables.tryFind(rules, new Predicate<AuthorizationPkgRule>() {
+                        @Override
+                        public boolean apply(AuthorizationPkgRule input) {
+ return input.getPermission().getCode().equalsIgnoreCase(permission.name());
+                        }
+                    }).isPresent()) {
+                        return true;
+                    }
+                }
+                break;
+            }
+        }
+
+        // fall back to application-logic rules.

         switch(permission) {

@@ -155,7 +189,7 @@
             case USER_CHANGEPASSWORD:
                 return
                         null!=authenticatedUser
- && (authenticatedUser.getIsRoot() || authenticatedUser.equals(target)); + && (authenticatedUser.getIsRoot() || authenticatedUser.equals(target));

             case USER_LIST:
return null!=authenticatedUser && authenticatedUser.getIsRoot();
=======================================
--- /haikudepotserver-webapp/src/main/resources/messages.properties Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages.properties Sat Jul 26 04:02:29 2014 UTC
@@ -38,7 +38,7 @@
 breadcrumb.listUsers.title=List Users
 breadcrumb.initiatePasswordReset.title=Password Reset
 breadcrumb.completePasswordReset.title=Password Reset
-breadcrumb.listAuthorizationPkgRules.title=Pkg Authorization Rules
+breadcrumb.listAuthorizationPkgRules.title=Package Authorization Rules
 breadcrumb.addAuthorizationPkgRule.title=Add Rule

 about.title=About
@@ -268,16 +268,16 @@
 listAuthorizationPkgRules.searchAction.title=Go
 listAuthorizationPkgRules.addRuleAction.title=Add rule
 listAuthorizationPkgRules.search.userNickname.title=User
-listAuthorizationPkgRules.search.pkgName.title=Pkg
+listAuthorizationPkgRules.search.pkgName.title=Package
 listAuthorizationPkgRules.noResults.title=No Results
-listAuthorizationPkgRules.noResults.description=There were no rules able to be found from your search criteria. +listAuthorizationPkgRules.noResults.description=No rules were found that fit your search criteria.
 listAuthorizationPkgRules.table.user.title=User
 listAuthorizationPkgRules.table.permission.title=Permission
 listAuthorizationPkgRules.table.pkg.title=Package
 listAuthorizationPkgRules.userNicknameNotFound.title=User Not Found
-listAuthorizationPkgRules.userNicknameNotFound.description=The user identified by the supplied nickname was unable to be found. +listAuthorizationPkgRules.userNicknameNotFound.description=There's no user registered with the supplied nickname.
 listAuthorizationPkgRules.pkgNameNotFound.title=Package Not Found
-listAuthorizationPkgRules.pkgNameNotFound.description=The package identified by the supplied name was unable to be found. +listAuthorizationPkgRules.pkgNameNotFound.description=There's no package with the supplied name.
 listAuthorizationPkgRules.deleteRuleModal.title=Delete Rule
listAuthorizationPkgRules.deleteRuleModal.description=Are you sure that you would like to delete this rule?
 listAuthorizationPkgRules.deleteRuleModal.cancelAction.title=Cancel
@@ -285,9 +285,9 @@
 listAuthorizationPkgRules.allPackages=All packages

 addAuthorizationPkgRule.userNickname.title=User Nickname
-addAuthorizationPkgRule.userNickname.required=A user to be associated with the new rule must be identified. +addAuthorizationPkgRule.userNickname.required=A new rule has to be associated with a registered user. addAuthorizationPkgRule.userNickname.pattern=The user nickname is malformed. -addAuthorizationPkgRule.userNickname.notfound=No user was able to be found with this nickname. +addAuthorizationPkgRule.userNickname.notfound=There's no user registered with the supplied nickname.
 addAuthorizationPkgRule.permission.title=Permission
 addAuthorizationPkgRule.authorizationTargetScopeType.apkg.title=A Package
addAuthorizationPkgRule.authorizationTargetScopeType.allpkgs.title=All Packages
@@ -295,11 +295,11 @@
 addAuthorizationPkgRule.pkgName.title=Package Name
 addAuthorizationPkgRule.pkgName.required=The package name is required.
 addAuthorizationPkgRule.pkgName.pattern=The package name is malformed.
-addAuthorizationPkgRule.pkgName.notfound=No package was able to be found with this name. +addAuthorizationPkgRule.pkgName.notfound=There's no package with the supplied name.
 addAuthorizationPkgRule.action.title=Add Rule
 addAuthorizationPkgRule.conflict.title=Conflict
addAuthorizationPkgRule.conflict.description=The rule that you tried to add conflicts with a rule that already \ - existed in the system. It may be that the exact same rule was already present or it may be that a less \ + exists. It may be that the exact same rule was already present or it may be that a less \
   specific rule already existed.

 banner.action.more=About Haiku Depot Server
=======================================
--- /haikudepotserver-webapp/src/main/resources/messages_de.properties Tue Jul 8 09:00:54 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages_de.properties Sat Jul 26 04:02:29 2014 UTC
@@ -36,6 +36,8 @@
 breadcrumb.listUsers.title=Benutzer anzeigen
 breadcrumb.initiatePasswordReset.title=Kennwort zurücksetzen
 breadcrumb.completePasswordReset.title=Kennwort zurücksetzen
+breadcrumb.listAuthorizationPkgRules.title=Paket Autorisierungsregeln
+breadcrumb.addAuthorizationPkgRule.title=Regel hinzufügen

 about.title=Über
about.mainDescription=Dies ist der Anwendungs-Server (Version {0}) "Haiku Depot Server", \
@@ -256,6 +258,41 @@

 listPkgVersionsForPkg.latest=Neueste bei Architektur

+listAuthorizationPkgRules.searchAction.title=Los
+listAuthorizationPkgRules.addRuleAction.title=Regel hinzufügen
+listAuthorizationPkgRules.search.userNickname.title=Benutzer
+listAuthorizationPkgRules.search.pkgName.title=Paket
+listAuthorizationPkgRules.noResults.title=Kein Ergebnis
+listAuthorizationPkgRules.noResults.description=Es wurden keine Regeln gefunden, die den eingegebenen Suchkriterien entsprechen.
+listAuthorizationPkgRules.table.user.title=Benutzer
+listAuthorizationPkgRules.table.permission.title=Zugriffsrecht
+listAuthorizationPkgRules.table.pkg.title=Paket
+listAuthorizationPkgRules.userNicknameNotFound.title=Benutzer nicht gefunden +listAuthorizationPkgRules.userNicknameNotFound.description=Es wurde kein Benutzer mit dem angegebenen Namen gefunden.
+listAuthorizationPkgRules.pkgNameNotFound.title=Paket nicht gefunden
+listAuthorizationPkgRules.pkgNameNotFound.description=Es wurde kein Paket mit dem angegebenen Namen gefunden.
+listAuthorizationPkgRules.deleteRuleModal.title=Regel löschen
+listAuthorizationPkgRules.deleteRuleModal.description=Soll diese Regel wirklich gelöscht werden?
+listAuthorizationPkgRules.deleteRuleModal.cancelAction.title=Abbruch
+listAuthorizationPkgRules.deleteRuleModal.confirmAction.title=Löschen
+listAuthorizationPkgRules.allPackages=Alle Pakete
+
+addAuthorizationPkgRule.userNickname.title=Benutzername
+addAuthorizationPkgRule.userNickname.required=Eine neue Regel muss einem registrierten Benutzer zugeordnet werden. +addAuthorizationPkgRule.userNickname.pattern=Der Benutzername scheint nicht das korrekte Format zu haben. +addAuthorizationPkgRule.userNickname.notfound=Es wurde kein Benutzer mit dem angegebenen Namen gefunden.
+addAuthorizationPkgRule.permission.title=Zugriffsrecht
+addAuthorizationPkgRule.authorizationTargetScopeType.apkg.title=Ein Paket
+addAuthorizationPkgRule.authorizationTargetScopeType.allpkgs.title=Alle Pakete
+addAuthorizationPkgRule.authorizationTargetScopeType.title=Zieltyp
+addAuthorizationPkgRule.pkgName.title=Paketname
+addAuthorizationPkgRule.pkgName.required=Es wird ein Paketname benötigt.
+addAuthorizationPkgRule.pkgName.pattern=Der Paketname scheint nicht das korrekte Format zu haben. +addAuthorizationPkgRule.pkgName.notfound=Es wurde kein Paket mit dem angegebenen Namen gefunden.
+addAuthorizationPkgRule.action.title=Regel hinzufügen
+addAuthorizationPkgRule.conflict.title=Konflikt
+addAuthorizationPkgRule.conflict.description=Die Regel, die hinzugefügt werden soll, kollidiert mit einer bereits bestehenden Regel. Entweder besteht die exakt gleiche Regel bereits, oder vielleicht eine nur weniger spezifische.
+
 banner.action.more=Über Haiku Depot Server
 banner.action.authenticate=Benutzer anmelden
 banner.action.createUser=Neuen Benutzer registrieren
@@ -264,6 +301,12 @@
 banner.action.naturalLanguagePrefix=Sprache:
 banner.action.repositories=Paket-Depots anzeigen
 banner.action.users=Benutzer anzeigen
+banner.action.authorizationPkgRules=Paket-Autorisierung
+
+permission.pkg_editicon.title=Paket-Icon bearbeiten
+permission.pkg_editscreenshot.title=Paket-Screenshots bearbeiten
+permission.pkg_editcategories.title=Paket-Kategorien bearbeiten
+permission.pkg_editversionlocalization.title=Lokalisierung von Paketversionen bearbeiten

 naturalLanguage.en=English
 naturalLanguage.de=Deutsch

==============================================================================
Revision: ed8ad4e8f4c6
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sat Jul 26 10:22:06 2014 UTC
Log: + possibility to download all of the authorization pkg rules as csv

http://code.google.com/p/haiku-depot-web-app/source/detail?r=ed8ad4e8f4c6

Added:
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/controller/AuthorizationPkgRuleController.java
Modified:
 /haikudepotserver-parent/pom.xml
 /haikudepotserver-webapp/pom.xml
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthenticationFilter.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java
 /haikudepotserver-webapp/src/main/resources/messages.properties
 /haikudepotserver-webapp/src/main/resources/messages_de.properties
 /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/entryPoint.jsp
/haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrules.html /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrulescontroller.js

=======================================
--- /dev/null
+++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/controller/AuthorizationPkgRuleController.java Sat Jul 26 10:22:06 2014 UTC
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014, Andrew Lindesay
+ * Distributed under the terms of the MIT License.
+ */
+
+package org.haikuos.haikudepotserver.security.controller;
+
+import com.google.common.base.Preconditions;
+import com.google.common.net.HttpHeaders;
+import com.google.common.net.MediaType;
+import org.apache.cayenne.ObjectContext;
+import org.apache.cayenne.configuration.server.ServerRuntime;
+import org.haikuos.haikudepotserver.dataobjects.User;
+import org.haikuos.haikudepotserver.security.AbstractUserAuthenticationAware; +import org.haikuos.haikudepotserver.security.AuthorizationPkgRuleOrchestrationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+@Controller
+@RequestMapping("/secured/authorization/authorizationpkgrule")
+public class AuthorizationPkgRuleController extends AbstractUserAuthenticationAware {
+
+ protected static Logger logger = LoggerFactory.getLogger(AuthorizationPkgRuleController.class);
+
+    @Resource
+    ServerRuntime serverRuntime;
+
+    @Resource
+ AuthorizationPkgRuleOrchestrationService authorizationPkgRuleOrchestrationService;
+
+    /**
+ * <p>This method will produce a CSV file containing all of the rules.</p>
+     */
+
+    @RequestMapping(value = "/download.csv", method = RequestMethod.GET)
+    public void download(
+            HttpServletResponse httpResponse
+    ) {
+
+        Preconditions.checkNotNull(httpResponse);
+
+        final ObjectContext context = serverRuntime.getContext();
+        User user = obtainAuthenticatedUser(context);
+
+        if(null==user || !user.getIsRoot()) {
+            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        }
+
+ httpResponse.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.CSV_UTF_8.toString()); + httpResponse.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=authorizationpkgrule.csv");
+
+        try {
+ authorizationPkgRuleOrchestrationService.generateCsv(context, httpResponse.getOutputStream());
+        }
+        catch(IOException ioe) {
+ throw new RuntimeException("unable to produce the authorizationpkgrule csv download", ioe);
+        }
+
+        logger.info("did generate the authorizationpkgrule csv download");
+
+    }
+
+}
=======================================
--- /haikudepotserver-parent/pom.xml    Thu Jul 24 11:35:00 2014 UTC
+++ /haikudepotserver-parent/pom.xml    Sat Jul 26 10:22:06 2014 UTC
@@ -88,6 +88,11 @@
                 <artifactId>freemarker</artifactId>
                 <version>2.3.20</version>
             </dependency>
+            <dependency>
+                <groupId>net.sf.opencsv</groupId>
+                <artifactId>opencsv</artifactId>
+                <version>2.3</version>
+            </dependency>

             <!-- WEB / API -->
             <dependency>
=======================================
--- /haikudepotserver-webapp/pom.xml    Sat Jul 12 08:36:41 2014 UTC
+++ /haikudepotserver-webapp/pom.xml    Sat Jul 26 10:22:06 2014 UTC
@@ -42,6 +42,10 @@
             <groupId>org.freemarker</groupId>
             <artifactId>freemarker</artifactId>
         </dependency>
+        <dependency>
+            <groupId>net.sf.opencsv</groupId>
+            <artifactId>opencsv</artifactId>
+        </dependency>

         <!-- LOGGING -->
         <dependency>
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthenticationFilter.java Sat Jun 28 10:23:44 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthenticationFilter.java Sat Jul 26 10:22:06 2014 UTC
@@ -34,6 +34,9 @@
* <p>This filter does not present back to the user an HTTP 401 (Authorization Required) as is the norm with * basic authentication method; it will simply fail the authentication and there will be no authenticated user
  * in the current request-response cycle.</p>
+ *
+ * <p>For some URLs (those starting with "/secured/"), it is also possible for the filter to look for a
+ * bearer token on the parameters.  This only works on a GET request.</p>
  */

 public class AuthenticationFilter implements Filter {
@@ -42,6 +45,10 @@

private static Pattern PATTERN_AUTHORIZATION_HEADER = Pattern.compile("^([A-Za-z0-9]+)\\s+(.+)$");

+    private static String PREFIX_PATH_SECURED = "/secured/";
+
+    private static String PARAM_BEARER_TOKEN = "hdsbtok";
+
     @Resource
     AuthenticationService authenticationService;

@@ -94,6 +101,21 @@
logger.warn("attempt to process an authorization header, but it is malformed :. ignoring");
             }
         }
+
+ // if the user was not authenticated on the header, under certain circumstances, it may be possible for + // the authentication to occur based on a parameter of the GET request (in the query).
+
+ if(!authenticatedUserObjectId.isPresent() && httpRequest.getMethod().equals("GET")) { + String filterPathInfo = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
+
+            if(filterPathInfo.startsWith(PREFIX_PATH_SECURED)) {
+ String param = httpRequest.getParameter(PARAM_BEARER_TOKEN);
+
+                if (!Strings.isNullOrEmpty(param)) {
+ authenticatedUserObjectId = authenticationService.authenticateByToken(param);
+                }
+            }
+        }

// now continue with the rest of the servlet filter chain, keeping the thread local

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Sat Jul 26 04:02:29 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Sat Jul 26 10:22:06 2014 UTC
@@ -5,6 +5,7 @@

 package org.haikuos.haikudepotserver.security;

+import au.com.bytecode.opencsv.CSVWriter;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
@@ -13,14 +14,22 @@
 import org.apache.cayenne.exp.Expression;
 import org.apache.cayenne.exp.ExpressionFactory;
 import org.apache.cayenne.query.EJBQLQuery;
+import org.apache.cayenne.query.Ordering;
 import org.apache.cayenne.query.SelectQuery;
+import org.apache.cayenne.query.SortOrder;
 import org.haikuos.haikudepotserver.dataobjects.*;
 import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;
import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRuleSearchSpecification;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;

+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.util.Collections;
 import java.util.List;

@@ -33,6 +42,8 @@

protected static Logger logger = LoggerFactory.getLogger(AuthorizationPkgRuleOrchestrationService.class);

+    private static int LIMIT_GENERATECSV = 100;
+
     @SuppressWarnings("UnusedParameters")
     private String prepareWhereClause(
             List<Object> parameterAccumulator,
@@ -255,6 +266,59 @@
         }

     }
+
+    /**
+ * <p>This method will generate a CSV dump of the rules. This might be downloaded from a web-browser.</p>
+     */
+
+    public void generateCsv(
+            ObjectContext context,
+            OutputStream outputStream) throws IOException {
+
+        Preconditions.checkNotNull(context);
+        Preconditions.checkNotNull(outputStream);
+
+ DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.basicDateTimeNoMillis();
+
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+        CSVWriter csvWriter = new CSVWriter(outputStreamWriter);
+
+        csvWriter.writeNext(new String[]{
+                "create-timestamp",
+                "user-nickname",
+                "user-active",
+                "permission-code",
+                "permission-name",
+                "pkg-name"
+        });
+
+        SelectQuery query = new SelectQuery(PermissionUserPkg.class);
+ query.addOrdering(new Ordering(PermissionUserPkg.USER_PROPERTY + "." + User.NICKNAME_PROPERTY, SortOrder.ASCENDING)); + query.addOrdering(new Ordering(PermissionUserPkg.PERMISSION_PROPERTY+ "." + Permission.CODE_PROPERTY, SortOrder.ASCENDING));
+        query.setFetchLimit(LIMIT_GENERATECSV);
+        List<PermissionUserPkg> rules;
+
+        do {
+            rules = context.performQuery(query);
+
+            for(PermissionUserPkg rule : rules) {
+                csvWriter.writeNext(new String[] {
+ dateTimeFormatter.print(rule.getCreateTimestamp().getTime()),
+                        rule.getUser().getNickname(),
+                        Boolean.toString(rule.getUser().getActive()),
+                        rule.getPermission().getCode(),
+                        rule.getPermission().getName(),
+                        null!=rule.getPkg() ? rule.getPkg().getName() : ""
+                });
+            }
+
+ query.setFetchOffset(query.getFetchOffset() + LIMIT_GENERATECSV);
+        }
+        while(rules.size() >= LIMIT_GENERATECSV);
+
+        csvWriter.flush();
+        outputStreamWriter.flush();
+    }


 }
=======================================
--- /haikudepotserver-webapp/src/main/resources/messages.properties Sat Jul 26 04:02:29 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages.properties Sat Jul 26 10:22:06 2014 UTC
@@ -267,6 +267,7 @@

 listAuthorizationPkgRules.searchAction.title=Go
 listAuthorizationPkgRules.addRuleAction.title=Add rule
+listAuthorizationPkgRules.downloadCsvAction.title=Download rules as CSV
 listAuthorizationPkgRules.search.userNickname.title=User
 listAuthorizationPkgRules.search.pkgName.title=Package
 listAuthorizationPkgRules.noResults.title=No Results
@@ -302,15 +303,15 @@
exists. It may be that the exact same rule was already present or it may be that a less \
   specific rule already existed.

-banner.action.more=About Haiku Depot Server
+banner.action.more=About 'Haiku Depot Server'
 banner.action.authenticate=User login
 banner.action.createUser=Register new user
 banner.action.user=View details for {0}
 banner.action.logout=Logout {0}
 banner.action.naturalLanguagePrefix=Language:
-banner.action.repositories=List Package Repositories
-banner.action.users=List Users
-banner.action.authorizationPkgRules=Package Authorization
+banner.action.repositories=List package repositories
+banner.action.users=List users
+banner.action.authorizationPkgRules=Package authorization

 naturalLanguage.en=English
 naturalLanguage.de=Deutsch
=======================================
--- /haikudepotserver-webapp/src/main/resources/messages_de.properties Sat Jul 26 04:02:29 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/messages_de.properties Sat Jul 26 10:22:06 2014 UTC
@@ -260,6 +260,7 @@

 listAuthorizationPkgRules.searchAction.title=Los
 listAuthorizationPkgRules.addRuleAction.title=Regel hinzufügen
+listAuthorizationPkgRules.downloadCsvAction.title=Regeln als CSV unterladen
 listAuthorizationPkgRules.search.userNickname.title=Benutzer
 listAuthorizationPkgRules.search.pkgName.title=Paket
 listAuthorizationPkgRules.noResults.title=Kein Ergebnis
=======================================
--- /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/entryPoint.jsp Sat Jul 12 08:36:41 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/WEB-INF/views/entryPoint.jsp Sat Jul 26 10:22:06 2014 UTC
@@ -30,6 +30,13 @@
 <div class="container">
     <div ng-view></div>
 </div>
+
+<%--
+This IFRAME can be used by application logic to cause a download to occur.
+--%>
+
+<iframe id="download-iframe" style="display:none"></iframe>
+
 </body>

 </html>
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrules.html Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrules.html Sat Jul 26 10:22:06 2014 UTC
@@ -102,6 +102,11 @@
             <a href="" ng-click="goAddRule()">
<message key="listAuthorizationPkgRules.addRuleAction.title"></message>
             </a>
+        </li>
+        <li>
+            <a href="" ng-click="goDownloadCsv()">
+ <message key="listAuthorizationPkgRules.downloadCsvAction.title"></message>
+            </a>
         </li>
     </ul>

=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrulescontroller.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/listauthorizationpkgrulescontroller.js Sat Jul 26 10:22:06 2014 UTC
@@ -59,6 +59,14 @@
                 return amFetchingRules;
             };

+            $scope.goDownloadCsv = function() {
+                if(!userState.user().token) {
+ throw Error('the user state token must be present to be able to authenticate the request');
+                }
+                var iframeEl = document.getElementById("download-iframe");
+ iframeEl.src = '/secured/authorization/authorizationpkgrule/download.csv?hdsbtok=' + userState.user().token;
+            }
+
             function refetchRules() {

                 amFetchingRules = true;

==============================================================================
Revision: a2303a6006e2
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sun Jul 27 10:42:54 2014 UTC
Log: + documentation update in relation to authorization, but some other small changes

http://code.google.com/p/haiku-depot-web-app/source/detail?r=a2303a6006e2

Added:
 /haikudepotserver-docs/src/main/latex/docs/part-security.tex
Modified:
 /haikudepotserver-docs/src/main/latex/docs/docs.tex
 /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf
 /haikudepotserver-docs/src/main/latex/docs/part-api.tex
 /haikudepotserver-docs/src/main/latex/docs/part-datamodel.tex
 /haikudepotserver-docs/src/main/latex/docs/part-overview.tex

=======================================
--- /dev/null
+++ /haikudepotserver-docs/src/main/latex/docs/part-security.tex Sun Jul 27 10:42:54 2014 UTC
@@ -0,0 +1,58 @@
+% -----------
+% Copyright 2014, Andrew Lindesay
+% Distributed under the terms of the MIT License.
+% -----------
+
+\section{Security}
+\label{security}
+
+This section aims to outline the security-system employed. The system does not use sessions and therefore exposes its web services in a stateless manner. This implies that each request-response cycle is considered in isolation.
+
+\subsection{Authentication}
+
+\fcolorbox{red}{white}{\parbox{\textwidth}{\color{red} In a production environment, transport to and from the application server {\bf must} use the ``https'' protocol in order to ensure that the payloads are not transmitted over networks in the clear. The payloads will contain sensitive authentication data.}}
+
+Authentication of invocations for both REST and JSON-RPC uses either ``basic authentication'' or ``token bearer authentication''. The application does not support cookie-based sessions. If the authentication fails then the request will continue unauthenticated; there is no `challenge' necessarily returned back to the caller. A special API exists to provide a means to test a username and password authentication for the purposes of a login panel or similar; {\it UserApi.authenticateUser(..)}. This method will return a token if the authentication was successful.
+
+\subsubsection{Basic Authentication}
+
+This approach involves an HTTP header being included in each invocation that identifies the user and also provides their password. The value of the header includes a base-64 encoded string of the username and password, separated by a colon. This is an example of such a header;
+
+\framebox{\tt Authorization: Basic dXNlcjpwYXNzd29yZA}
+
+\subsubsection{Token Bearer}
+\label{token-bearer-authentication}
+
+This is a system of authentication that relies on a token being supplied with a client API invocation to the server. The client will first use the API {\it UserApi.authenticateUser(..)} to initially authenticate and obtain a token. This token can then be used to authenticate further API calls by including it in an HTTP header. An (abbreviated) example of the header might be;
+
+\framebox{\tt Authorization: Bearer eyJhbGciOiJIUzI.eyJleHAiOjE0MDM5NDY.1ifnDTLvX3A}
+
+The format and structure of the token conforms with the {\it json web token} standard. These tokens are signed using a secret, will expire after a certain length of time and are specific to a given deployment environment. The deployment environment is identified by configuring an ``issuer''. See \ref{config} for further details on configuration of these values.
+
+As the token has an expiry, an API method {\it UserApi.renewToken(..)} exists to obtain a fresh token before the existing token expires. The expiry can be read from the token; read about {\it json web tokens} to find out how they work.
+
+The token bearer system of authentication has the advantage that although a user's password is supplied by the user to the client software for the initial authentication, subsequent requests do not need to convey the password. In addition, the token expire and are unable to be employed after they have expired.
+
+\paragraph{Token Bearer on GET Queries}
+
+Certain HTTP requests may be authenticated using a URL query parameter. This approach only applies when the URL path has a prefix ``{\tt /secured/}'' and when the URL is employed with the HTTP ``GET'' method. The query parameter key is ``{\tt hdsbtok}'' and the value is a {\it json web token} as described above.
+
+This approach is sub-optimal because, although the token expires, it may be present in the browser's history for some time during which it is valid.
+
+\subsection{Authorization}
+
+A user's ability to undertake some action is controlled by the authorization system. In a given context, a user will be involved and a permission will be in question. The enumeration {\tt Permission} contains a list of the permissions that are enforced by the application. Each permission is either with respect to the currently authenticated user or some target object. An example of some permissions and their targets are;
+
+\begin{tabular}{ | l | l | }
+\hline
+Permission & Target \\
+\hline
+{\tt USER\_CHANGEPASSWORD} & A User \\
+{\tt PKG\_EDITSCREENSHOT} & A Pkg \\
+{\tt REPOSITORY\_LIST} & {\it Current User} \\
+\hline
+\end{tabular}
+
+There are inherent authorization rules that are encoded in the implementation of the method {\tt AuthorizationService.check(..)} and there are also configurable rules that can be setup for certain permissions in relation to packages. These are stored in the database via the entity {\tt PermissionUserPkg}. Note that a given {\tt PermissionUserPkg} may have a null package associated with it and in this case, the rule applies to all packages.
+
+As part of an API invocation, an HTTP status 401 or 403 response maybe returned should authorization checks fail.
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/docs.tex Mon Jun 16 09:40:17 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/docs.tex Sun Jul 27 10:42:54 2014 UTC
@@ -57,6 +57,7 @@
 \input{part-overview}
 \input{part-datamodel}
 \input{part-userrating}
+\input{part-security}
 \input{part-config}
 \input{part-buildandrelease}
 \input{part-deployment}
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/img-datamodel.pdf Sun Jul 27 10:42:54 2014 UTC
Binary file, no diff available.
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-api.tex Sat Jun 28 10:23:44 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-api.tex Sun Jul 27 10:42:54 2014 UTC
@@ -1,5 +1,5 @@
 % -----------
-% Copyright 2013, Andrew Lindesay
+% Copyright 2013-2014, Andrew Lindesay
 % Distributed under the terms of the MIT License.
 % -----------

@@ -33,32 +33,9 @@

The term ``invocation'' refers to a request-response cycle from the client software into the application server over the HTTP protocol. Each API invocation is made in a {\it stateless} manner in that each invocation is not dependent on the prior invocation.

-\subsubsection{Authentication}
+\subsubsection{Security}

-\fcolorbox{red}{white}{\parbox{\textwidth}{\color{red} In a production environment, transport to and from the application server {\bf must} use the ``https'' protocol in order to ensure that the payloads are not transmitted over networks in the clear. The payloads will contain sensitive authentication data.}}
-
-Authentication of invocations for both REST and JSON-RPC uses ``basic authentication'' or ``token bearer authentication''. The application does not support cookie-based authentication. As part of an API invocation, an HTTP status 401 or 403 response maybe returned should authorization checks fail. If the authentication fails then the request will continue unauthenticated; there is no `challenge' necessarily returned back to the caller. A special API exists to provide a means to test a username and password authentication for the purposes of a login panel or similar; {\it UserApi.authenticateUser(..)}. This method will return a token if the authentication was successful.
-
-\paragraph{Basic Authentication}
-
-This approach involves an HTTP header being included in each invocation that identifies the user and also provides their password. The value of the header includes a base-64 encoded string of the username and password, separated by a colon. This is an example of such a header;
-
-\framebox{\tt Authorization: Basic dXNlcjpwYXNzd29yZA}
-
-\paragraph{Token Bearer}
-\label{token-bearer-authentication}
-
-This is a system of authentication that relies on a token being supplied with a client API invocation to the server. The client will first use the API {\it UserApi.authenticateUser(..)} to initially authenticate and obtain a token. This token can then be used to authenticate further API calls by including it in an HTTP header. An (abbreviated) example of the header might be;
-
-\framebox{\tt Authorization: Bearer eyJhbGciOiJIUzI.eyJleHAiOjE0MDM5NDY.1ifnDTLvX3A}
-
-The format and structure of the token conforms with the {\it json web token} standard. These tokens are signed using a secret, will expire after a certain length of time and are specific to a given deployment environment. The deployment environment is identified by configuring an ``issuer''.
-
-See \ref{config} for further details on configuration of these values.
-
-As the token has an expiry, an API method {\it UserApi.renewToken(..)} exists to obtain a fresh token before the existing token expires. The expiry can be read from the token; read about {\it json web tokens} to find out how they work.
-
-The token bearer system of authentication has the advantage that a user's password is supplied by the user to the client software for the initial authentication, but subsequently the password does not need to reside in the client runtime for the remainder of that session. +See \ref{security} for details on how to authenticate API requests as well as how API requests are authorized.

 \subsection{JSON-RPC API}

=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-datamodel.tex Sat Jul 12 10:17:07 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-datamodel.tex Sun Jul 27 10:42:54 2014 UTC
@@ -13,4 +13,4 @@
 \label{\thefigure}
 \end{figure}

-Figure {\thefigure} shows the approximate persisted data model for the application server. Note that not all of this data model may be implemented; it is a {\it work in progress}. +Figure {\thefigure} shows the persisted data model for the application server.
=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-overview.tex Mon Jun 16 09:40:17 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-overview.tex Sun Jul 27 10:42:54 2014 UTC
@@ -35,7 +35,7 @@
 \label{prerequisites}

 \begin{itemize}
-\item \href{https://community.java.net/open-jdk}{Java} $\geqslant$ 1.6
+\item \href{https://community.java.net/open-jdk}{Java} $\geqslant$ 1.7
 \item \href{http://maven.apache.org}{Maven} $\geqslant$ 3.0.3
 \item \href{http://www.postgres.org}{Postgres} database $\geqslant$ 9.1
 \end{itemize}

==============================================================================
Revision: 64aa6420db79
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Sun Jul 27 10:55:51 2014 UTC
Log:      + convert javascript throws from string to error object

http://code.google.com/p/haiku-depot-web-app/source/detail?r=64aa6420db79

Modified:
 /haikudepotserver-webapp/src/main/webapp/js/app/constants.js
/haikudepotserver-webapp/src/main/webapp/js/app/controller/addeditrepositorycontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/addedituserratingcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/authenticateusercontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/changepasswordcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/completepasswordresetcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/createusercontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalizationcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/editusercontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/initiatepasswordresetcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformationcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/activeindicatordirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/highlightedtextdirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationarrowdirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgcategorylabeldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgicondirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgversionlabeldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/ratingindicatordirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/repositorylabeldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/userlabeldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/directive/versionlabeldirective.js /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbfactoryservice.js /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/jsonrpcservice.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/messagesourceservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/pkgscreenshotservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgservice.js
/haikudepotserver-webapp/src/main/webapp/js/app/service/referencedataservice.js
 /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js

=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/constants.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/constants.js Sun Jul 27 10:55:51 2014 UTC
@@ -53,11 +53,11 @@
nextMatchSearchExpression: function(str, offset, searchExpression, searchExpressionType) {

             if(!searchExpressionType||!searchExpressionType.length) {
-                throw 'the search expression type must be supplied';
+                throw Error('the search expression type must be supplied');
             }

             if(null==offset || offset < 0) {
-                throw 'an offset is required';
+                throw Error('an offset is required');
             }

             if(str&&
@@ -79,7 +79,7 @@
                         break;

                     default:
- throw 'unknown search expression type; ' + searchExpressionType; + throw Error('unknown search expression type; ' + searchExpressionType);
                 }

             }
@@ -119,7 +119,7 @@
             targetIdentifier) {

             if(null==targetType && targetIdentifier) {
- throw 'if the target type is null (check on principal) then the target identifier is also expected to be null'; + throw Error('if the target type is null (check on principal) then the target identifier is also expected to be null');
             }

             if(!permissionCode||(!targetIdentifier&&null!=targetType)) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/addeditrepositorycontroller.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/addeditrepositorycontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -107,7 +107,7 @@
             $scope.goSave = function() {

                 if($scope.addEditRepositoryForm.$invalid) {
- throw 'expected the save of a repository to only to be possible if the form is valid'; + throw Error('expected the save of a repository to only to be possible if the form is valid');
                 }

                 amSaving = true;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/addedituserratingcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/addedituserratingcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -17,7 +17,7 @@
             errorHandling,referenceData,pkg,messageSource) {

             if(!userState.user()) {
- throw 'a user is required to add / edit user ratings on a package'; + throw Error('a user is required to add / edit user ratings on a package');
             }

             $scope.workingUserRating = undefined;
@@ -157,7 +157,7 @@
                             { code : code });

                         if(!naturalLanguageOption) {
- throw 'unable to find the natural language option for; ' + code; + throw Error('unable to find the natural language option for; ' + code);
                         }

                         return naturalLanguageOption;
@@ -169,7 +169,7 @@
                             { code : code });

                         if(!userRatingStabilityOption) {
- throw 'unable to find the user rating stability option for; ' + code; + throw Error('unable to find the user rating stability option for; ' + code);
                         }

                         return userRatingStabilityOption;
@@ -251,7 +251,7 @@
                                             // one.

                                             if(!pkg.versions[0].isLatest) {
- throw 'it is only possible to add a user rating to the latest version of a package.'; + throw Error('it is only possible to add a user rating to the latest version of a package.');
                                             }

// turn the package data inside out so that we have a pkgVersion data structure.
@@ -331,7 +331,7 @@
                 }

                 if($scope.addEditUserRatingForm.$invalid) {
- throw 'expected the save of a user rating to only to be possible if the form is valid'; + throw Error('expected the save of a user rating to only to be possible if the form is valid');
                 }

// there is the possibility that the user has attempted to create a user rating with no data! This
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/authenticateusercontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/authenticateusercontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -13,7 +13,7 @@
jsonRpc,constants,userState,errorHandling,breadcrumbs,breadcrumbFactory) {

             if(userState.user()) {
- throw 'it is not possible to enter the authenticate user controller with a currently authenticated user'; + throw Error('it is not possible to enter the authenticate user controller with a currently authenticated user');
             }

             $scope.didFailAuthentication = false;
@@ -61,7 +61,7 @@
             $scope.goAuthenticate = function() {

                 if($scope.authenticateUserForm.$invalid) {
- throw 'expected the authentication of a user only to be possible if the form is valid'; + throw Error('expected the authentication of a user only to be possible if the form is valid');
                 }

                 $scope.didFailAuthentication = false;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/changepasswordcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/changepasswordcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -110,7 +110,7 @@
             $scope.goChangePassword = function() {

                 if($scope.changePasswordForm.$invalid) {
- throw 'expected the change password of a user only to be possible if the form is valid'; + throw Error('expected the change password of a user only to be possible if the form is valid');
                 }

                 $scope.amChangingPassword = true;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/completepasswordresetcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/completepasswordresetcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -13,7 +13,7 @@
jsonRpc,constants,breadcrumbs,breadcrumbFactory,userState,errorHandling) {

             if(userState.user()) {
- throw 'it is not possible to complete password reset with an authenticated user'; + throw Error('it is not possible to complete password reset with an authenticated user');
             }

             var Status = {
@@ -99,7 +99,7 @@
             $scope.goResetPassword = function() {

                 if($scope.completePasswordResetData.$invalid) {
- throw 'expected the reset password only to be possible if the form is valid'; + throw Error('expected the reset password only to be possible if the form is valid');
                 }

                 $scope.status = Status.UNDERTAKING;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/createusercontroller.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/createusercontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -140,7 +140,7 @@
             $scope.goCreateUser = function() {

                 if($scope.createUserForm.$invalid) {
- throw 'expected the creation of a user only to be possible if the form is valid'; + throw Error('expected the creation of a user only to be possible if the form is valid');
                 }

                 $scope.amSaving = true;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgiconcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -103,7 +103,7 @@
             $scope.goStorePkgIcons = function() {

                 if($scope.editPkgIconForm.$invalid) {
- throw 'expected the editing of package icons only to be possible if the form is valid'; + throw Error('expected the editing of package icons only to be possible if the form is valid');
                 }

                 $scope.amSaving = true;
@@ -166,7 +166,7 @@
                                                         break;

                                                     default:
- throw 'expected size; ' + error.data.size; + throw Error('expected size; ' + error.data.size);
                                                 }
                                                 break;

@@ -175,12 +175,12 @@
                                                 break;

                                             default:
- throw 'unexpected media type code; ' + err.data.mediaTypeCode; + throw Error('unexpected media type code; ' + err.data.mediaTypeCode);

                                         }
                                     }
                                     else {
- throw 'expected data to be supplied with a bad pkg icon'; + throw Error('expected data to be supplied with a bad pkg icon');
                                     }

                                     break;
@@ -213,21 +213,21 @@
                     function dataUrlToBase64(u) {

                         if(!u) {
- throw 'the data url must be supplied to convert to base64'; + throw Error('the data url must be supplied to convert to base64');
                         }

                         if(0!= u.indexOf('data:')) {
- throw 'the data url was unable to be converted to base64 because it does not look like a data url'; + throw Error('the data url was unable to be converted to base64 because it does not look like a data url');
                         }

                         var commaI = u.indexOf(',');

                         if(-1==commaI) {
- throw 'expecting comma in data url to preceed the base64 data'; + throw Error('expecting comma in data url to preceed the base64 data');
                         }

if(!_.indexOf(u.substring(5,commaI).split(';'),'base64')) { - throw 'expecting base64 to appear in the data url'; + throw Error('expecting base64 to appear in the data url');
                         }

                         return u.substring(commaI+1);
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgscreenshotscontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -114,7 +114,7 @@
             $scope.goAddPkgScreenshot = function() {

                 if($scope.addPkgScreenshotForm.$invalid) {
- throw 'expected the editing of package screenshots only to be possible if the form is valid'; + throw Error('expected the editing of package screenshots only to be possible if the form is valid');
                 }

                 $scope.amCommunicating = true;
@@ -216,7 +216,7 @@
                 switch(i) {

                     case -1:
- throw 'unable to find the screenshot to re-order in the list of screenshots'; + throw Error('unable to find the screenshot to re-order in the list of screenshots');

                     case 0:
                         // already at the start
@@ -237,7 +237,7 @@
                 switch(i) {

                     case -1:
- throw 'unable to find the screenshot to re-order in the list of screenshots'; + throw Error('unable to find the screenshot to re-order in the list of screenshots');

                     case $scope.pkgScreenshots.length-1:
                         // already at the end
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalizationcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editpkgversionlocalizationcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -42,7 +42,7 @@
                 );

                 if(!result) {
-                    throw 'was not able to find the original translation';
+ throw Error('was not able to find the original translation');
                 }

                 return result;
@@ -95,7 +95,7 @@

             $scope.goChooseTranslation = function(translation) {
                 if(!translation) {
-                    throw 'the translation must be provided to select';
+ throw Error('the translation must be provided to select');
                 }

                 $scope.selectedTranslation = translation;
@@ -228,7 +228,7 @@
             $scope.saveEditedLocalizations = function() {

                  if(!$scope.canSave()) {
-                     throw 'not possible to save edited localizations';
+ throw Error('not possible to save edited localizations');
                  }

                 $scope.amSaving = true;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/editusercontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/editusercontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -108,7 +108,7 @@
             $scope.goSave = function() {

                 if($scope.editUserForm.$invalid) {
- throw 'expected the save of a user to only to be possible if the form is valid'; + throw Error('expected the save of a user to only to be possible if the form is valid');
                 }

                 amSaving = true;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/initiatepasswordresetcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/initiatepasswordresetcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -13,7 +13,7 @@
jsonRpc,constants,errorHandling,userState,breadcrumbs,breadcrumbFactory) {

             if(userState.user()) {
- throw 'it is not possible to reset the password if a user is presently authenticated.'; + throw Error('it is not possible to reset the password if a user is presently authenticated.');
             }

             var Status = {
@@ -84,7 +84,7 @@
             $scope.goInitiatePasswordReset = function() {

                 if($scope.initiatePasswordResetForm.$invalid) {
- throw 'expected the initiation of password reset only to be possible if the form is valid'; + throw Error('expected the initiation of password reset only to be possible if the form is valid');
                 }

                 $scope.status = Status.UNDERTAKING;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformationcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/runtimeinformationcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -51,7 +51,7 @@
             // ERROR HANDLING TESTING

             $scope.goRaiseExceptionInLocalRuntime = function() {
-                throw 'test exception in javascript environment';
+                throw Error('test exception in javascript environment');
             };

             $scope.goRaiseExceptionInServerRuntime = function() {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/viewpkgcontroller.js Sun Jul 27 10:55:51 2014 UTC
@@ -203,7 +203,7 @@
                                 var v0 = $scope.pkg.versions[0];

if(ur.pkgVersion.pkg.name != $scope.pkg.name) { - throw 'illegal; a user rating is being shown for another package'; + throw Error('illegal; a user rating is being shown for another package');
                                 }

ur.isOtherVersion = ur.pkgVersion.major != v0.major ||
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/activeindicatordirective.js Sun Jun 8 11:49:56 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/activeindicatordirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -16,7 +16,7 @@
             var stateExpression = attributes['state'];

             if(!stateExpression || !stateExpression.length) {
-                throw 'a value for the binding \'state\' was expected';
+ throw Error('a value for the binding \'state\' was expected');
             }

var svgE = angular.element('<svg width="12px" height="12px"><circle cx="6px" cy="6px" r="5.5px" fill="gray"></circle></svg>');
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/highlightedtextdirective.js Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/highlightedtextdirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -26,15 +26,15 @@
var searchExpressionTypeExpression = attributes['searchExpressionType'];

             if(!valueExpression || !valueExpression.length) {
-                throw 'the value expression must be supplied';
+                throw Error('the value expression must be supplied');
             }

if(!searchExpressionExpression | | !searchExpressionExpression.length) {
-                throw 'the search expression expression must be supplied';
+ throw Error('the search expression expression must be supplied');
             }

if(!searchExpressionTypeExpression | | !searchExpressionTypeExpression.length) { - throw 'the search expression type expression must be supplied'; + throw Error('the search expression type expression must be supplied');
             }

             var containerE = angular.element('<span></span>');
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationarrowdirective.js Fri May 9 11:55:44 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationarrowdirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -32,7 +32,7 @@
                             break;

                         default:
- throw 'illegal direction on pagination arrow; '+direction; + throw Error('illegal direction on pagination arrow; '+direction);
                     }

                     // replaces the element supplied with the SVG one.
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js Sun Jun 8 12:12:43 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/paginationcontroldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -32,11 +32,11 @@
                     }

                     if(result < 3) {
- throw 'a link count of ' + result + ' is not possible for the pagination control - it must be >= 3'; + throw Error('a link count of ' + result + ' is not possible for the pagination control - it must be >= 3');
                     }

                     if(0 == result % 2) {
- throw 'a link count of ' + result + ' is not possible for the pagination control - it must be an odd number'; + throw Error('a link count of ' + result + ' is not possible for the pagination control - it must be an odd number');
                     }

                     return result;
@@ -89,7 +89,7 @@
var newOffsetStr = event.target.getAttribute('pagination-offset');

                             if(!newOffsetStr || !newOffsetStr.length) {
- throw 'unable to get the selected offset for the target of the pagination event'; + throw Error('unable to get the selected offset for the target of the pagination event');
                             }

                             $scope.$apply(function() {
@@ -137,15 +137,15 @@
                 function refreshPageControlsWithValues(total,offset,max) {

                     if(max <= 0) {
- throw 'the \'max\' value must be a positive integer'; + throw Error('the \'max\' value must be a positive integer');
                     }

                     if(offset < 0) {
-                        throw 'the \'offset\' must be >= 0';
+                        throw Error('the \'offset\' must be >= 0');
                     }

                     if(offset >= total) {
-                        throw 'the \'offset\' must be < '+total;
+                        throw Error('the \'offset\' must be < '+total);
                     }

var pages = Math.floor((total / max) + (total % max ? 1 : 0));
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgcategorylabeldirective.js Sun Jun 8 12:12:43 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgcategorylabeldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -22,7 +22,7 @@
                 var pkgCategoryExpression = attributes['pkgCategory'];

if (!pkgCategoryExpression | | !pkgCategoryExpression.length) { - throw 'expected expression for "pkgCategoryExpression"'; + throw Error('expected expression for "pkgCategoryExpression"');
                 }

                 $scope.$watch(pkgCategoryExpression, function(newValue) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgicondirective.js Mon Jun 9 10:41:05 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgicondirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -20,17 +20,17 @@
                     var pkgExpression = attributes['pkg'];

                     if (!pkgExpression || !pkgExpression.length) {
- throw 'the pkg binding must be an expression to a package'; + throw Error('the pkg binding must be an expression to a package');
                     }

                     if (!size) {
-                        throw 'the size binding must be supplied';
+                        throw Error('the size binding must be supplied');
                     }

                     size = parseInt('' + size, 10);

                     if (size < 1 || size > 1000) {
-                        throw 'preposterous value for size; ' + size;
+ throw Error('preposterous value for size; ' + size);
                     }

var el = angular.element('<img src="" width="' + size + '" height="' + size + '"></img>');
@@ -41,7 +41,7 @@

                         if (pkg) {
                             if (!pkg.name||!pkg.name.length) {
- throw 'the package is expected to have a name in order to derive an icon url'; + throw Error('the package is expected to have a name in order to derive an icon url');
                             }

url = pkgIcon.url(pkg, constants.MEDIATYPE_PNG, size);
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgversionlabeldirective.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/pkgversionlabeldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -33,7 +33,7 @@
                     var pkgVersionBreadcrumbItem = undefined;

if(!pkgVersionExpression | | !pkgVersionExpression.length) {
-                        throw 'expected expression for "pkgVersion"';
+ throw Error('expected expression for "pkgVersion"');
                     }

                     var containerEl = angular.element('<span></span>');
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/ratingindicatordirective.js Sat May 24 11:47:09 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/ratingindicatordirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -41,15 +41,15 @@
                             topLevelE.removeClass('app-hide');

                             if (!angular.isNumber(rating)) {
-                                throw 'the value supplied is not a number';
+ throw Error('the value supplied is not a number');
                             }

                             if (rating < 0) {
- throw 'the value supplied for a rating indicator is less than zero'; + throw Error('the value supplied for a rating indicator is less than zero');
                             }

                             if (rating > 5) {
- throw 'the value supplied for a rating indicator is more than five'; + throw Error('the value supplied for a rating indicator is more than five');
                             }

                             rating *= 2;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/repositorylabeldirective.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/repositorylabeldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -28,7 +28,7 @@
                 var shouldLink = attributes['shouldLink'];

if (!repositoryExpression || !repositoryExpression.length) {
-                    throw 'the repository expression should be defined';
+ throw Error('the repository expression should be defined');
                 }

                 $scope.$watch(repositoryExpression, function (repository) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/userlabeldirective.js Tue Jul 8 11:11:57 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/userlabeldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -21,7 +21,7 @@
var shouldLink = undefined == attributes['shouldLink'] | | 'true' == shouldLink;

                 if(!userExpression || !userExpression.length) {
-                    throw 'expected expression for "user"';
+                    throw Error('expected expression for "user"');
                 }

                 var containerEl = angular.element('<span></span>');
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/directive/versionlabeldirective.js Wed May 28 12:18:40 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/directive/versionlabeldirective.js Sun Jul 27 10:55:51 2014 UTC
@@ -25,7 +25,7 @@
                     var versionExpression = attributes['version'];

                     if(!versionExpression || !versionExpression.length) {
-                        throw 'expected expression for "version"';
+                        throw Error('expected expression for "version"');
                     }

                     var containerEl = angular.element('<span></span>');
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbfactoryservice.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbfactoryservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -19,15 +19,15 @@

function generateBaseUrlForPkg(pkgName,versionCoordinates,architectureCode) {
                 if(!pkgName||!pkgName.length) {
-                    throw 'the package name must be supplied';
+                    throw Error('the package name must be supplied');
                 }

                 if(!versionCoordinates||!versionCoordinates.major) {
-                    throw 'version coordinates must be supplied';
+                    throw Error('version coordinates must be supplied');
                 }

                 if(!architectureCode||!architectureCode.length) {
- throw 'the architectureCode must be supplied to create a view pkg'; + throw Error('the architectureCode must be supplied to create a view pkg');
                 }

                 var parts = [
@@ -64,7 +64,7 @@

             function latestVersion(pkgVersions) {
                 if(!pkgVersions || !pkgVersions.length) {
- throw 'a package version is required to get the latest'; + throw Error('a package version is required to get the latest');
                 }

var pkgVersion = _.findWhere(pkgVersions, { isLatest : true } );
@@ -78,7 +78,7 @@

function createManipulatePkgBreadcrumbItem(pkgWithVersion0, pathSuffix, titlePortion) { if(!pkgWithVersion0 || !pkgWithVersion0.versions | | !pkgWithVersion0.versions.length) { - throw 'a package version is required to form a breadcrumb'; + throw Error('a package version is required to form a breadcrumb');
                 }

                 var pkgVersion = latestVersion(pkgWithVersion0.versions);
@@ -125,7 +125,7 @@

             function applyCurrentLocation(item) {
                 if(!item) {
- throw 'was expecting an item to be supplied to be augmented'; + throw Error('was expecting an item to be supplied to be augmented');
                 }

                 item.path = $location.path();
@@ -181,7 +181,7 @@

                 createCompletePasswordReset : function(token) {
                     if(!token||!token.length) {
- throw 'unable to create complete password reset without a token'; + throw Error('unable to create complete password reset without a token');
                     }

                     return applyDefaults({
@@ -206,7 +206,7 @@

                 createEditUser : function(user) {
                     if(!user||!user.nickname) {
- throw 'user with nickname is required to make this breadcrumb'; + throw Error('user with nickname is required to make this breadcrumb');
                     }

                     return applyDefaults({
@@ -232,7 +232,7 @@

                 createEditUserRating : function(userRating) {
                     if(!userRating || !userRating.code) {
-                        throw 'a user rating code is expected';
+                        throw Error('a user rating code is expected');
                     }

                     return applyDefaults({
@@ -311,7 +311,7 @@

createViewPkgWithSpecificVersionFromPkgVersion : function(pkgVersion) {
                     if(!pkgVersion || !pkgVersion.pkg) {
- throw 'a package version is required to form a breadcrumb'; + throw Error('a package version is required to form a breadcrumb');
                     }

                     return createViewPkgBreadcrumbItem(
@@ -328,7 +328,7 @@

                 createViewPkgWithSpecificVersionFromPkg : function(pkg) {
                     if(!pkg || !pkg.versions.length) {
- throw 'a package with a package version are required to form a breadcrumb'; + throw Error('a package with a package version are required to form a breadcrumb');
                     }

                     var pkgVersion = latestVersion(pkg.versions);
@@ -342,7 +342,7 @@
createViewPkgWithSpecificVersionFromRouteParams : function(routeParams) {

                     if(!routeParams||!routeParams.major) {
-                        throw 'route params are expected';
+                        throw Error('route params are expected');
                     }

                     return createViewPkgBreadcrumbItem(
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -32,26 +32,26 @@
             function verifyItem(item) {

                 if(!item) {
-                    throw 'a breadcrumb item was expected';
+                    throw Error(('a breadcrumb item was expected');
                 }

                 if(!item.titleKey || !item.titleKey.length) {
-                    throw 'a breadcrumb item must have a title key';
+                    throw Error('a breadcrumb item must have a title key');
                 }

                 if(!item.path || !item.path.length) {
-                    throw 'a breadcrumb item must have a path';
+                    throw Error('a breadcrumb item must have a path');
                 }

                 if(!item.search || !item.search.bcguid) {
-                    throw 'a "bcguid" is expected on a breadcrumb item';
+ throw Error('a "bcguid" is expected on a breadcrumb item');
                 }
             }

             function verifyItems(items) {

                 if(!items) {
-                    throw 'breadcrumb items were expected';
+                    throw Error('breadcrumb items were expected');
                 }

                 _.each(items, function(item) {
@@ -111,7 +111,7 @@

             function pop() {
                 if(!stack||!stack.length) {
-                    throw 'attempt to pop from empty breadcrumb stack';
+ throw Error('attempt to pop from empty breadcrumb stack');
                 }

                 var item = stack.pop();
@@ -126,7 +126,7 @@

             function popTo(item) {
                 if(!stack) {
- throw 'the breadcrumb stack is empty; not possible to popTo(..)'; + throw Error('the breadcrumb stack is empty; not possible to popTo(..)');
                 }

                 while(stack.length && stack[stack.length-1] != item) {
@@ -134,7 +134,7 @@
                 }

                 if(!stack.length) {
- throw 'the item requested to popTo was not found on the breadcrumb stack'; + throw Error('the item requested to popTo was not found on the breadcrumb stack');
                 }

                 $rootScope.$broadcast('breadcrumbChangeSuccess',stack);
@@ -166,7 +166,7 @@

             function mergeCompleteStack(stackIn) {
                 if(!stackIn || !stackIn.length) {
- throw 'attempt to merge an empty stack into the existing stack; not possible'; + throw Error('attempt to merge an empty stack into the existing stack; not possible');
                 }

                 verifyItems(stackIn);
@@ -259,7 +259,7 @@
                     pop();

                     if(!stack.length) {
- throw 'have popped from the stack, but there is now nothing to navigate to'; + throw Error('have popped from the stack, but there is now nothing to navigate to');
                     }

                     var top = peek();
@@ -280,7 +280,7 @@
                     var result = popTo(item);

                     if(!result) {
-                        throw 'unable to find the item on the stack';
+ throw Error('unable to find the item on the stack');
                     }

                     navigateTo(result);
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/jsonrpcservice.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/jsonrpcservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -47,7 +47,7 @@
                 setHeader : function(name, value) {

                     if(!name || 0==''+name.length) {
-                        throw 'the name of the http header is required';
+ throw Error('the name of the http header is required');
                     }

                     if(!value || 0==''+value.length) {
@@ -76,11 +76,11 @@
                 call : function(endpoint, method, params, id) {

                     if(!endpoint) {
- throw 'the endpoint is required to invoke a json-rpc method'; + throw Error('the endpoint is required to invoke a json-rpc method');
                     }

                     if(!method) {
- throw 'the method is required to invoke a json-rpc method'; + throw Error('the method is required to invoke a json-rpc method');
                     }

                     if(!params) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/messagesourceservice.js Tue Jul 1 10:10:47 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/messagesourceservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -35,7 +35,7 @@

             function getMessages(naturalLanguageCode) {
                 if(!naturalLanguageCode || !naturalLanguageCode.length) {
- throw 'the natural language code should be supplied to get messages'; + throw Error('the natural language code should be supplied to get messages');
                 }

                 var deferred = $q.defer();
@@ -92,11 +92,11 @@

             function getMessage(naturalLanguageCode, key) {
                 if(!naturalLanguageCode || !naturalLanguageCode.length) {
- throw 'the natural language code should be supplied to get a message'; + throw Error('the natural language code should be supplied to get a message');
                 }

                 if(!key || !key.length) {
-                    throw 'a key must be supplied to get a message';
+                    throw Error('a key must be supplied to get a message');
                 }

                 var deferred = $q.defer();
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgiconservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -21,13 +21,13 @@
                 url : function(pkg, mediaTypeCode, size) {

                     if(!pkg) {
- throw 'the pkg must be supplied to get the package icon url'; + throw Error('the pkg must be supplied to get the package icon url');
                     }

                     var u = '/pkgicon/' + pkg.name;

                     if(!mediaTypeCode) {
- throw 'the media type code is required to get the package icon url'; + throw Error('the media type code is required to get the package icon url');
                     }

                     switch(mediaTypeCode) {
@@ -37,7 +37,7 @@

                         case constants.MEDIATYPE_PNG: {
                             if(!size || !(32==size||16==size)) {
- throw 'the size is not valid for obtaining the package icon url'; + throw Error('the size is not valid for obtaining the package icon url');
                             }

                             u += '.png?f=true&s=' + size
@@ -45,7 +45,7 @@
                             break;

                         default:
-                            throw 'unknown media type; ' + mediaTypeCode;
+ throw Error('unknown media type; ' + mediaTypeCode);
                     }

                     if(pkg.modifyTimestamp) {
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgscreenshotservice.js Fri Mar 28 10:40:14 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgscreenshotservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -38,7 +38,7 @@
                 setHeader : function(name, value) {

                     if(!name || 0==''+name.length) {
-                        throw 'the name of the http header is required';
+ throw Error('the name of the http header is required');
                     }

                     if(!value || 0==''+value.length) {
@@ -55,11 +55,11 @@
                 addScreenshot : function(pkg, screenshotFile) {

                     if(!pkg) {
- throw 'the pkg must be supplied to add a pkg screenshot'; + throw Error('the pkg must be supplied to add a pkg screenshot');
                     }

                     if(!screenshotFile) {
- throw 'to add a screenshot for '+pkg.name+' the image file must be provided'; + throw Error('to add a screenshot for '+pkg.name+' the image file must be provided');
                     }

                     var deferred = $q.defer();
@@ -77,7 +77,7 @@
var code = header('X-HaikuDepotServer-ScreenshotCode');

                             if(!code || !code.length) {
- throw 'the screenshot code should have been supplied back from creating a new screenshot'; + throw Error('the screenshot code should have been supplied back from creating a new screenshot');
                             }

                             deferred.resolve(code);
@@ -116,11 +116,11 @@

                 rawUrl : function(pkg, code) {
                     if(!pkg) {
- throw 'the pkg must be supplied to get a package screenshot url'; + throw Error('the pkg must be supplied to get a package screenshot url');
                     }

                     if(!code || !code.length) {
- throw 'the code must be supplied to derive a url for the screenshot image'; + throw Error('the code must be supplied to derive a url for the screenshot image');
                     }

                     return '/pkgscreenshot/' + code + '/raw';
@@ -134,11 +134,11 @@

                 url : function(pkg, code, targetWidth, targetHeight) {
                     if(!pkg) {
- throw 'the pkg must be supplied to get a package screenshot url'; + throw Error('the pkg must be supplied to get a package screenshot url');
                     }

                     if(!code || !code.length) {
- throw 'the code must be supplied to derive a url for the screenshot image'; + throw Error('the code must be supplied to derive a url for the screenshot image');
                     }

                     var u = '/pkgscreenshot/' + code + '.png';
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgservice.js Tue Jul 1 10:10:47 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/pkgservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -22,15 +22,15 @@
function getPkgWithSpecificVersion(pkgName, versionCoordinates, architectureCode, incrementCounter) {

                 if(!pkgName||!pkgName.length) {
-                    throw 'pkg name must be supplied';
+                    throw Error('pkg name must be supplied');
                 }

                 if(!versionCoordinates||!versionCoordinates.major) {
-                    throw 'version coordinates must be supplied';
+                    throw Error('version coordinates must be supplied');
                 }

                 if(!architectureCode||!architectureCode.length) {
-                    throw 'architecture code must be supplied';
+                    throw Error('architecture code must be supplied');
                 }

                 var deferred = $q.defer();
@@ -73,7 +73,7 @@
function getPkgWithSpecificVersionFromRouteParams(routeParams, incrementCounter) {

                 if(!routeParams) {
-                    throw 'route params expected';
+                    throw Error('route params expected');
                 }

                 return getPkgWithSpecificVersion(
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/referencedataservice.js Tue Jul 1 10:10:47 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/referencedataservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -31,7 +31,7 @@
             function getData(what) {

                 if(!what || !what.length) {
- throw 'the method name is expected in order to get reference data'; + throw Error('the method name is expected in order to get reference data');
                 }

                 var deferred = $q.defer();
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/userstateservice.js Sun Jul 27 10:55:51 2014 UTC
@@ -51,7 +51,7 @@
                 }
                 else {
                     if(millisUntilExpirationForToken(token) <= 0) {
- throw 'at attempt has been made to set a token that has expired already'; + throw Error('at attempt has been made to set a token that has expired already');
                     }

if (userStateData.user && userStateData.user.nickname == tokenNickname(token)) {
@@ -103,7 +103,7 @@
var firstMs = timestampsOfLastTokenRenewals.shift();

if(nowMs - firstMs < MIN_MILLIS_FOR_TIMESTAMPS_OF_LAST_TOKEN_RENEWALS) { - throw '10 or more renewals of tokens in < ' + MIN_MILLIS_FOR_TIMESTAMPS_OF_LAST_TOKEN_RENEWALS + 'ms -- something wrong; failing'; + throw Error('10 or more renewals of tokens in < ' + MIN_MILLIS_FOR_TIMESTAMPS_OF_LAST_TOKEN_RENEWALS + 'ms -- something wrong; failing');
                                         }
                                     }

@@ -159,13 +159,13 @@

             function tokenClaimSet(token) {
                 if(!token||!token.length) {
-                    throw 'missing json web token';
+                    throw Error('missing json web token');
                 }

                 var parts = token.split('.');

                 if(3 != parts.length) {
- throw 'json web token should contain three dot-separated parts'; + throw Error('json web token should contain three dot-separated parts');
                 }

                 return angular.fromJson(window.atob(parts[1]));
@@ -180,7 +180,7 @@
                 var claimSet = tokenClaimSet(token);

if(!claimSet || !claimSet.exp | | !angular.isNumber(claimSet.exp)) { - throw 'malformed claim set; unable to get the \'exp\' data'; + throw Error('malformed claim set; unable to get the \'exp\' data');
                 }

                 return new Date(claimSet.exp * 1000);
@@ -195,14 +195,14 @@
                 var claimSet = tokenClaimSet(token);

                 if(!claimSet || !claimSet.sub) {
- throw 'malformed claim set; unable to get the \'sub\' data'; + throw Error('malformed claim set; unable to get the \'sub\' data');
                 }

                 var sub = '' + claimSet.sub;
                 var suffixIndex = sub.indexOf('@hds');

                 if(-1==suffixIndex) {
-                    throw 'malformed nickname in token; missing suffix';
+ throw Error('malformed nickname in token; missing suffix');
                 }

                 return sub.substring(0,suffixIndex);
@@ -225,11 +225,11 @@
                 else {

                     if(!value.nickname) {
- throw 'the nickname is required when setting a user'; + throw Error('the nickname is required when setting a user');
                     }

                     if(!value.token) {
- throw 'the json web token is required when setting a user'; + throw Error('the json web token is required when setting a user');
                     }

                     userStateData.user = { nickname : value.nickname };
@@ -251,15 +251,15 @@
             function validateTargetAndPermissions(targetAndPermissions) {
_.each(targetAndPermissions, function(targetAndPermission) { if(undefined === targetAndPermission.targetType | | !_.contains(['PKG','USER','REPOSITORY','USERRATING',null],targetAndPermission.targetType)) {
-                        throw 'illegal argument; bad targetType supplied';
+ throw Error('illegal argument; bad targetType supplied');
                     }

if(undefined === targetAndPermission.targetIdentifier) { - throw 'illegal argument; bad targetIdentifier supplied'; + throw Error('illegal argument; bad targetIdentifier supplied');
                     }

                     if(!targetAndPermission.permissionCode) {
-                        throw 'illegal argument; bad permission code';
+ throw Error('illegal argument; bad permission code');
                     }
                 })
             }
@@ -333,7 +333,7 @@
// might be able to be a bit smarter about this in the future.

                             if(!uncachedTargetAndPermissions.length) {
- throw 'illegal state; top-most request has no uncached target and permissions'; + throw Error('illegal state; top-most request has no uncached target and permissions');
                             }

                             jsonRpc.call(
@@ -352,7 +352,7 @@
result = tryDeriveFromCache(request.targetAndPermissions);

                                     if(!result) {
- throw 'illegal state; was not able to resolve the request from cache after fetching from application server'; + throw Error('illegal state; was not able to resolve the request from cache after fetching from application server');
                                     }

                                     request.deferred.resolve(result);
@@ -421,7 +421,7 @@
                 if(undefined !== value) {

                     if(!value || !value.match(/^[a-z]{2}$/)) {
- throw 'the value \''+value+'\' is not a valid natural language code'; + throw Error('the value \''+value+'\' is not a valid natural language code');
                     }

                     if(userStateData.naturalLanguageCode != value) {

==============================================================================
Revision: 8b13fffe6056
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 09:04:02 2014 UTC
Log:      + add create / modify on pkg version localizations
+ fix issue with search on home page
+ updated dependency versions

http://code.google.com/p/haiku-depot-web-app/source/detail?r=8b13fffe6056

Modified:
 /haikudepotserver-parent/pom.xml
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgVersionLocalization.java /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/cayenne/PostAddCreateAndModifyTimestampListener.java
 /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml
/haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.11__Authorization_2.sql
 /haikudepotserver-webapp/src/main/webapp/js/app/controller/home.html
/haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js

=======================================
--- /haikudepotserver-parent/pom.xml    Sat Jul 26 10:22:06 2014 UTC
+++ /haikudepotserver-parent/pom.xml    Mon Jul 28 09:04:02 2014 UTC
@@ -10,8 +10,8 @@

         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

-        <spring.version>4.0.5.RELEASE</spring.version>
-        <postgresql.version>9.3-1101-jdbc41</postgresql.version>
+        <spring.version>4.0.6.RELEASE</spring.version>
+        <postgresql.version>9.3-1102-jdbc41</postgresql.version>

 <!--
The extensions seem to appear in the maven coordinates, but not in the paths
@@ -24,7 +24,7 @@
         <web-underscorejs.versionbase>1.6.0</web-underscorejs.versionbase>
<web-underscorejs.versionextension>-3</web-underscorejs.versionextension>

-        <web-momentjs.versionbase>2.6.0</web-momentjs.versionbase>
+        <web-momentjs.versionbase>2.7.0</web-momentjs.versionbase>
         <web-momentjs.versionextension>-2</web-momentjs.versionextension>

         <nimbus-jose-jwt.version>2.26</nimbus-jose-jwt.version>
@@ -130,7 +130,7 @@
             <dependency>
                 <groupId>com.googlecode.flyway</groupId>
                 <artifactId>flyway-core</artifactId>
-                <version>2.3.1</version>
+                <version>2.3.1</version> <!-- 3.x available -->
             </dependency>
             <dependency>
                 <groupId>org.apache.cayenne</groupId>
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java Mon Apr 7 11:46:03 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/PkgVersionLocalization.java Mon Jul 28 09:04:02 2014 UTC
@@ -6,8 +6,9 @@
 package org.haikuos.haikudepotserver.dataobjects;

import org.haikuos.haikudepotserver.dataobjects.auto._PkgVersionLocalization; +import org.haikuos.haikudepotserver.dataobjects.support.CreateAndModifyTimestamped;

-public class PkgVersionLocalization extends _PkgVersionLocalization {
+public class PkgVersionLocalization extends _PkgVersionLocalization implements CreateAndModifyTimestamped {

     public boolean equalsForContent(PkgVersionLocalization other) {
         return
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgVersionLocalization.java Mon Mar 10 10:18:38 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/dataobjects/auto/_PkgVersionLocalization.java Mon Jul 28 09:04:02 2014 UTC
@@ -1,5 +1,7 @@
 package org.haikuos.haikudepotserver.dataobjects.auto;

+import java.util.Date;
+
 import org.haikuos.haikudepotserver.dataobjects.NaturalLanguage;
 import org.haikuos.haikudepotserver.dataobjects.PkgVersion;
 import org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject;
@@ -12,19 +14,35 @@
  */
 public abstract class _PkgVersionLocalization extends AbstractDataObject {

+ public static final String CREATE_TIMESTAMP_PROPERTY = "createTimestamp";
     public static final String DESCRIPTION_PROPERTY = "description";
+ public static final String MODIFY_TIMESTAMP_PROPERTY = "modifyTimestamp";
     public static final String SUMMARY_PROPERTY = "summary";
public static final String NATURAL_LANGUAGE_PROPERTY = "naturalLanguage";
     public static final String PKG_VERSION_PROPERTY = "pkgVersion";

     public static final String ID_PK_COLUMN = "id";

+    public void setCreateTimestamp(Date createTimestamp) {
+        writeProperty(CREATE_TIMESTAMP_PROPERTY, createTimestamp);
+    }
+    public Date getCreateTimestamp() {
+        return (Date)readProperty(CREATE_TIMESTAMP_PROPERTY);
+    }
+
     public void setDescription(String description) {
         writeProperty(DESCRIPTION_PROPERTY, description);
     }
     public String getDescription() {
         return (String)readProperty(DESCRIPTION_PROPERTY);
     }
+
+    public void setModifyTimestamp(Date modifyTimestamp) {
+        writeProperty(MODIFY_TIMESTAMP_PROPERTY, modifyTimestamp);
+    }
+    public Date getModifyTimestamp() {
+        return (Date)readProperty(MODIFY_TIMESTAMP_PROPERTY);
+    }

     public void setSummary(String summary) {
         writeProperty(SUMMARY_PROPERTY, summary);
=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/cayenne/PostAddCreateAndModifyTimestampListener.java Wed Jun 4 11:36:29 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/support/cayenne/PostAddCreateAndModifyTimestampListener.java Mon Jul 28 09:04:02 2014 UTC
@@ -39,6 +39,8 @@
         callbackRegistry.addListener(Repository.class, this);
         callbackRegistry.addListener(User.class, this);
         callbackRegistry.addListener(UserRating.class, this);
+        callbackRegistry.addListener(PkgVersionLocalization.class, this);
+
     }

     @Override
=======================================
--- /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/HaikuDepot.map.xml Mon Jul 28 09:04:02 2014 UTC
@@ -189,8 +189,10 @@
                </db-key-generator>
        </db-entity>
        <db-entity name="pkg_version_localization" schema="haikudepot">
+ <db-attribute name="create_timestamp" type="TIMESTAMP" isMandatory="true"/>
                <db-attribute name="description" type="VARCHAR" length="8192"/>
<db-attribute name="id" type="BIGINT" isPrimaryKey="true" isMandatory="true"/> + <db-attribute name="modify_timestamp" type="TIMESTAMP" isMandatory="true"/> <db-attribute name="natural_language_id" type="BIGINT" isMandatory="true"/>
                <db-attribute name="pkg_version_id" type="BIGINT"/>
                <db-attribute name="summary" type="VARCHAR" length="8192"/>
@@ -368,7 +370,9 @@
<obj-attribute name="body" type="java.lang.String" lock="true" db-attribute-path="body"/>
        </obj-entity>
<obj-entity name="PkgVersionLocalization" className="org.haikuos.haikudepotserver.dataobjects.PkgVersionLocalization" lock-type="optimistic" dbEntityName="pkg_version_localization" superClassName="org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject"> + <obj-attribute name="createTimestamp" type="java.sql.Timestamp" db-attribute-path="create_timestamp"/> <obj-attribute name="description" type="java.lang.String" lock="true" db-attribute-path="description"/> + <obj-attribute name="modifyTimestamp" type="java.sql.Timestamp" db-attribute-path="modify_timestamp"/> <obj-attribute name="summary" type="java.lang.String" lock="true" db-attribute-path="summary"/>
        </obj-entity>
<obj-entity name="PkgVersionUrl" className="org.haikuos.haikudepotserver.dataobjects.PkgVersionUrl" lock-type="optimistic" dbEntityName="pkg_version_url" superClassName="org.haikuos.haikudepotserver.dataobjects.support.AbstractDataObject">
=======================================
--- /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.11__Authorization_2.sql Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/main/resources/db/haikudepot/migration/V1.11__Authorization_2.sql Mon Jul 28 09:04:02 2014 UTC
@@ -25,3 +25,14 @@

ALTER TABLE haikudepot.permission_user_pkg ADD COLUMN create_timestamp TIMESTAMP NOT NULL; ALTER TABLE haikudepot.permission_user_pkg ALTER COLUMN pkg_id DROP NOT NULL;
+
+-- ------------------------------------------------------
+-- SNEAK IN A COUPLE OF ADDITIONAL CHANGES...
+-- ------------------------------------------------------
+
+ALTER TABLE haikudepot.pkg_version_localization ADD COLUMN modify_timestamp TIMESTAMP; +ALTER TABLE haikudepot.pkg_version_localization ADD COLUMN create_timestamp TIMESTAMP; +UPDATE haikudepot.pkg_version_localization pvl SET modify_timestamp=(SELECT create_timestamp FROM haikudepot.pkg_version pv WHERE pv.id=pvl.pkg_version_id); +UPDATE haikudepot.pkg_version_localization pvl SET create_timestamp=modify_timestamp; +ALTER TABLE haikudepot.pkg_version_localization ALTER COLUMN modify_timestamp SET NOT NULL; +ALTER TABLE haikudepot.pkg_version_localization ALTER COLUMN create_timestamp SET NOT NULL;
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/controller/home.html Sun Jun 29 11:22:04 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/controller/home.html Mon Jul 28 09:04:02 2014 UTC
@@ -27,9 +27,7 @@
                     autocomplete="off"
                     return-key-press="goSearch()"
                     placeholder="zlib"></input>
-            <button
- ng-disabled="!searchExpression | | !searchExpression.length"
-                    ng-click="goSearch()">
+            <button ng-click="goSearch()">
                 <message key="home.searchButton.title"></message>
             </button>
         </div>
=======================================
--- /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Sun Jul 27 10:55:51 2014 UTC +++ /haikudepotserver-webapp/src/main/webapp/js/app/service/breadcrumbsservice.js Mon Jul 28 09:04:02 2014 UTC
@@ -32,7 +32,7 @@
             function verifyItem(item) {

                 if(!item) {
-                    throw Error(('a breadcrumb item was expected');
+                    throw Error('a breadcrumb item was expected');
                 }

                 if(!item.titleKey || !item.titleKey.length) {

==============================================================================
Revision: 2ba59138f557
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 10:13:14 2014 UTC
Log:      + additional integration tests for authorization

http://code.google.com/p/haiku-depot-web-app/source/detail?r=2ba59138f557

Modified:
/haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java

=======================================
--- /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Sat Jul 26 10:22:06 2014 UTC +++ /haikudepotserver-webapp/src/main/java/org/haikuos/haikudepotserver/security/AuthorizationPkgRuleOrchestrationService.java Mon Jul 28 10:13:14 2014 UTC
@@ -17,10 +17,12 @@
 import org.apache.cayenne.query.Ordering;
 import org.apache.cayenne.query.SelectQuery;
 import org.apache.cayenne.query.SortOrder;
-import org.haikuos.haikudepotserver.dataobjects.*;
+import org.haikuos.haikudepotserver.dataobjects.Permission;
+import org.haikuos.haikudepotserver.dataobjects.PermissionUserPkg;
+import org.haikuos.haikudepotserver.dataobjects.Pkg;
+import org.haikuos.haikudepotserver.dataobjects.User;
 import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRule;
import org.haikuos.haikudepotserver.security.model.AuthorizationPkgRuleSearchSpecification;
-import org.joda.time.format.DateTimeFormat;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 import org.slf4j.Logger;
=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java Thu Jul 24 11:35:00 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java Mon Jul 28 10:13:14 2014 UTC
@@ -85,6 +85,83 @@
assertTargetAndPermission(data, result.targetAndPermissions.get(0), true);

     }
+
+    /**
+ * <P>With a user-pkg rule missing we should see this authorization come through in a check
+     * for that permission against the package being false.</P>
+     */
+
+    @Test
+    public void checkAuthorizationRequest_asUserWithoutRule() {
+        integrationTestSupportService.createStandardTestData();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ integrationTestSupportService.createBasicUser(context, "testuser1", "fakepassword");
+        }
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                "pkg1",
+                Permission.PKG_EDITICON.name()));
+
+        setAuthenticatedUser("testuser1");
+
+        // ------------------------------------
+ CheckAuthorizationResult result = authorizationApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + Assertions.assertThat(result.targetAndPermissions.get(0).authorized).isFalse();
+
+    }
+
+    /**
+ * <P>With a user-pkg rule in place we should see this authorization come through in a check
+     * for that permission against the package being true.</P>
+     */
+
+    @Test
+    public void checkAuthorizationRequest_asUserWithRule() {
+        integrationTestSupportService.createStandardTestData();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ User user1 = integrationTestSupportService.createBasicUser(context, "testuser1", "fakepassword");
+            Pkg pkg1 = Pkg.getByName(context, "pkg1").get();
+
+ org.haikuos.haikudepotserver.dataobjects.Permission permission = + org.haikuos.haikudepotserver.dataobjects.Permission.getByCode(context, Permission.PKG_EDITICON.name().toLowerCase()).get();
+
+ PermissionUserPkg pup_u1p1 = context.newObject(PermissionUserPkg.class);
+            pup_u1p1.setPkg(pkg1);
+            pup_u1p1.setUser(user1);
+            pup_u1p1.setPermission(permission);
+
+            context.commitChanges();
+        }
+
+ CheckAuthorizationRequest request = new CheckAuthorizationRequest();
+        request.targetAndPermissions = Lists.newArrayList();
+
+ request.targetAndPermissions.add(new CheckAuthorizationRequest.AuthorizationTargetAndPermission(
+                AuthorizationTargetType.PKG,
+                "pkg1",
+                Permission.PKG_EDITICON.name()));
+
+        setAuthenticatedUser("testuser1");
+
+        // ------------------------------------
+ CheckAuthorizationResult result = authorizationApi.checkAuthorization(request);
+        // ------------------------------------
+
+ Assertions.assertThat(result.targetAndPermissions.size()).isEqualTo(1); + Assertions.assertThat(result.targetAndPermissions.get(0).authorized).isTrue();
+
+    }

     /**
* <p>This test checks to see what happens if you try to create a new rule, but you are not

==============================================================================
Revision: 81ce1686ef67
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 10:34:56 2014 UTC
Log:      + small documentation correction

http://code.google.com/p/haiku-depot-web-app/source/detail?r=81ce1686ef67

Modified:
 /haikudepotserver-docs/src/main/latex/docs/part-security.tex

=======================================
--- /haikudepotserver-docs/src/main/latex/docs/part-security.tex Sun Jul 27 10:42:54 2014 UTC +++ /haikudepotserver-docs/src/main/latex/docs/part-security.tex Mon Jul 28 10:34:56 2014 UTC
@@ -33,15 +33,13 @@

The token bearer system of authentication has the advantage that although a user's password is supplied by the user to the client software for the initial authentication, subsequent requests do not need to convey the password. In addition, the token expire and are unable to be employed after they have expired.

-\paragraph{Token Bearer on GET Queries}
+\subsubsection{Token Bearer on GET Queries}

Certain HTTP requests may be authenticated using a URL query parameter. This approach only applies when the URL path has a prefix ``{\tt /secured/}'' and when the URL is employed with the HTTP ``GET'' method. The query parameter key is ``{\tt hdsbtok}'' and the value is a {\it json web token} as described above.

-This approach is sub-optimal because, although the token expires, it may be present in the browser's history for some time during which it is valid.
-
 \subsection{Authorization}

-A user's ability to undertake some action is controlled by the authorization system. In a given context, a user will be involved and a permission will be in question. The enumeration {\tt Permission} contains a list of the permissions that are enforced by the application. Each permission is either with respect to the currently authenticated user or some target object. An example of some permissions and their targets are; +A user's ability to undertake some action is controlled by the authorization system. In a given context, a user and a permission will be considered. The enumeration {\tt Permission} contains a list of the permissions that are enforced by the application. Each permission is either with respect to the currently authenticated user or some target object. Examples of some permissions and the type of target objects that they apply to are;

 \begin{tabular}{ | l | l | }
 \hline

==============================================================================
Revision: 61975821316a
Author:   Andrew Lindesay <apl@xxxxxxxxxxxxxx>
Date:     Mon Jul 28 11:00:19 2014 UTC
Log:      + additional authorization test

http://code.google.com/p/haiku-depot-web-app/source/detail?r=61975821316a

Modified:
/haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java

=======================================
--- /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java Mon Jul 28 10:13:14 2014 UTC +++ /haikudepotserver-webapp/src/test/java/org/haikuos/haikudepotserver/api1/AuthorizationApiIT.java Mon Jul 28 11:00:19 2014 UTC
@@ -195,6 +195,39 @@
         catch(AuthorizationFailureException afe) {
             // expected
         }
+
+    }
+
+    /**
+ * <p>Checks what happens if you try to create a new rule, but you are not root.</p>
+     */
+
+    @Test
+ public void testCreateAuthorizationRule_notAuthorized() throws ObjectNotFoundException, AuthorizationRuleConflictException {
+        integrationTestSupportService.createStandardTestData();
+
+        {
+            ObjectContext context = serverRuntime.getContext();
+ integrationTestSupportService.createBasicUser(context,"testuser","fakepassword");
+        }
+
+        setAuthenticatedUser("testuser");
+
+ CreateAuthorizationPkgRuleRequest request = new CreateAuthorizationPkgRuleRequest(); + request.permissionCode = Permission.PKG_EDITSCREENSHOT.name().toLowerCase();
+        request.pkgName = "pkg1";
+        request.userNickname = "testuser";
+
+        try {
+            // ------------------------------------
+            authorizationApi.createAuthorizationPkgRule(request);
+            // ------------------------------------
+
+ Assert.fail("it was expected that an authorization error would arise.");
+        }
+        catch(AuthorizationFailureException afe) {
+            logger.info("authorization failed as expected");
+        }

     }

Other related posts:

  • » [haiku-depot-web] [haiku-depot-web-app] 9 new revisions pushed by haiku.li...@xxxxxxxxx on 2014-07-28 11:00 GMT - haiku-depot-web-app