AccessControlException при вызове метода с использованием JAAS

Я создал приложение клиент/сервер с помощью JAAS. Вход в систему работает нормально, так как вход в систему успешен. Именно при попытке предоставить разрешения определенным методам AccessController начинает приводить AccessControlException.

 java.security.AccessControlException: access denied ("myPackage.CustomPermission" "someMethod")

Класс разрешения:

public class CustomPermission extends BasicPermission {

    public CustomPermission(String name) {
        super(name);
    }

    public CustomPermission(String name, String method) {
        super(name, method);
    }
}

У меня также есть собственный основной класс:

public class CustomPrincipal implements Principal {
    private String name;

    public CustomPrincipal(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }
}

При входе пользователь указывает имя пользователя и пароль. В БД имена пользователей и пароли имеют связанный с ними «уровень пользователя». Эти пользовательские уровни используются в качестве имен-участников, добавляемых к Subject, созданному при входе в систему. Я добавляю его при обработке commit-метода в LoginModule:

public boolean commit() throws LoginException {
    if(status == ConfigValues.NOT || subject == null)
        return false;

    principal = new CustomPrincipal(userlvl);

    if(subject.getPrincipals().add(principal)) {

        username = null;
        password = null;

        status = ConfigValues.COMMIT;

        return true;
    }

    return false;
}

Ради этого я создаю процедуру входа в систему следующим образом:

LoginContext lc = new LoginContext("MyLoginModule", new RemoteCallbackHandler(username, password));

lc.login();

new ServerImpl(lc.getSubject());

Затем Subject используется в реализации «прокси-сервера» для проверки разрешений, например так (user = lc.getSubject):

public String someMethod() throws RemoteException, PrivilegedActionException {
    return Subject.doAs(user, new PrivilegedExceptionAction<String>() {
        @Override
        public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
            AccessController.checkPermission(new CustomPermission("someMethod"));
            return realServerImplObj.someMethod();
        }
    });
}

.policy-файл:

grant codeBase "file:./bin/-" Principal myPackage.CustomPrincipal "user" {
    permission myPackage.CustomPermission "someMethod";
};

«Пользователь», конечно же, является одним из уровней пользователя, с которым вы можете войти в систему.

Я попытался добавить дополнительные гранты, например:

grant codeBase "file:./bin/-" {
    permission javax.security.auth.AuthPermission "createLoginContext.MyLoginModule";
    permission javax.security.auth.AuthPermission "doAs";
};

У меня также есть конфигурация для LoginModule:

MyLoginModule {
    myPackage.MyLoginModule required debug=true;
};

Я установил свойства перед всем этим, конечно: редактировать: файлы расположены в корне проекта

System.setProperty("java.security.auth.login.config", "file:./MyConfig.config");
System.setProperty("java.security.policy", "file:./MyPolicy.policy");

Сервер запускается с аргументом -Djava.security.manager. Клиент не использует какие-либо аргументы, файлы конфигурации или политики.

Я попытался удалить codeBase, чтобы убедиться, что мой путь неверен. Если я добавлю permissions java.util.AllPermissions, то все в порядке (но... конечно, это не хорошо, так как это определенно не так, как задумано). Что я делаю неправильно здесь? Я предполагаю, что это комбинация реализации Principal-, Permission, .policiy- и LoginModule.

РЕДАКТИРОВАТЬ Исключение вызывается при вызове AccessController.checkPermissions(...) в реализации "прокси-сервера".

РЕДАКТИРОВАТЬ 2 Я пробовал следующее редактирование кода:

return AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
    @Override
    public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
        //AccessController.checkPermission(new CustomPermission("someMethod"));
        return realServerImplObj.someMethod();
    }
});

Обратите внимание, что я изменил Subject.doAs(user, new Privile.... на AccessController.doPrivileged(new Privilege. user больше не анализируется в параметре, и мы используем статический метод AccessController.doPrivileged, а не Subject.doAs. Еще одно примечание: //AccessController.checkPermission(new CustomPer... был прокомментирован.

Но теперь каждый может вызывать любой метод, поскольку мы фактически никогда не проверяем разрешения. Это как если бы AccessController никогда не знал о разрешениях, предоставленных в файле .policy.

РЕДАКТИРОВАТЬ 3 Казалось, проблема связана с реализацией Принципала.

Просто для уточнения, вот какие изменения были внесены:

@Override
public String someMethod() throws PrivilegedActionException {
    return Subject.doAsPrivileged(user, new PrivilegedExceptionAction<String>() {
        @Override
        public String run() throws PrivilegedActionException, RemoteException, ServerNotActiveException {
            AccessController.checkPermission(new CustomPermission("someMethod"));
            return realServerImplObj.someMethod();
        }
    }, null);
}

Разница return Subject.doAsPrivileged(user, ... , null);. Обратите внимание на ноль в конце.

Реализовано два метода в классе CustomPrincipal, #equals(Object) и #hashCode(). См. здесь можно найти универсальный пример обоих методов и образец Principal реализации в целом.

Также добавлено (хотя на самом деле кажется, что оно работает без) следующее: файл .policy

grant codeBase "file:./bin/-" {
    permission javax.security.auth.AuthPermission "createLoginContext.MyLoginModule";
    permission javax.security.auth.AuthPermission "doAs";
    permission javax.security.auth.AuthPermission "doAsPrivileged";
};

permission javax.security.auth.AuthPermission "doAsPrivileged"; — новая добавленная запись.


person eli    schedule 22.11.2016    source источник
comment
Попробуйте присвоить AuthPermission("doAsPrivileged") файлу:./bin/- и подставить Subject.doAs вместо Subject.doAsPrivileged(..., null).   -  person Uux    schedule 24.11.2016
comment
... замена Subject.doAsPrivileged(...,null) на Subject.doAs - это то, что я на самом деле имел в виду выше.   -  person Uux    schedule 24.11.2016
comment
@Uux Это превращается в ошибку неоднозначного типа для Темы. doAsPrivileged также принимает 3 параметра: (Subject, PrivilegedAction<T>, AccessControlContext). Я пробовал с помощью doAsPrivileged параметры (user, null, AccessController.getContext). Я не понимаю, чего вы пытаетесь достичь, делая это (игнорируя мои предыдущие заметки; допустим, это было возможно). Можете ли вы уточнить?   -  person eli    schedule 24.11.2016
comment
@Uux У меня только что был прорыв, я обновил свой вопрос в ОП.   -  person eli    schedule 24.11.2016
comment
Я удалил свою правку, я забежал вперед. Это не сработало, как предполагалось. :-)   -  person eli    schedule 24.11.2016
comment
Извините за расплывчатость; Я имел в виду Subject#doAsPrivileged(yourUser, yourAction, null). Здесь важно значение null AccessControlContext, поскольку оно эффективно настраивает Permissions кадра стека после вызова doAsPrivileged на точно те, которые были предоставлены yourUser. Обратите внимание: приведет ли это к повышению или снижению привилегий, зависит исключительно от того, какие yourUser были предоставлены на уровне Policy.   -  person Uux    schedule 24.11.2016
comment
Да, прочитав ваш комментарий несколько раз, я действительно провел тест с нулевым объектом в параметре AccessControlContext. Однако без везения. :-(   -  person eli    schedule 24.11.2016
comment
Следующее предположение: пусть ваш Principal реализует #hashCode() и #equals(Object).   -  person Uux    schedule 24.11.2016
comment
Это сработало. Дагит, конечно. Я предполагаю, что он использует метод equals для оценки между новым разрешением, которое мы создаем в параметре checkPermission, и созданным в файле .policy? Сейчас у меня просто очень и очень странный глюк. Некоторые пользователи имеют больше прав, чем должны. попробую поработать. Если вы сделаете ответ, я поставлю его как правильный. :-)   -  person eli    schedule 24.11.2016
comment
Ну, нет, я предполагаю, что это деталь реализации по умолчанию Policy (sun.security.provider.PolicyFile), которая в отсутствие Principal#equals не может проверить, что активный Principal эквивалентен тому, что в его grant утверждениях . Можете ли вы объяснить, что вы подразумеваете под дополнительными разрешениями? Каким будет их ожидаемый набор разрешений? Что им на самом деле дают?   -  person Uux    schedule 24.11.2016
comment
Конечно, я не знаю, почему я говорю о разрешениях, поскольку очевидно, что принципы были исправлены, хе-хе.   -  person eli    schedule 24.11.2016
comment
И не говоря уже о проблеме с разрешениями. Был небольшой умысел с моей стороны - во время редактирования кода некоторые методы были лишены AccessController.checkPermission. В основном никаких проверок разрешений не проводилось. ;-) Можешь ответить? Я проголосую + установим его как правильный ответ, так как он работает сейчас.   -  person eli    schedule 24.11.2016


Ответы (1)


Subject#doAs vs Subject#doAsPrivileged

Алгоритм авторизации по умолчанию, используемый AccessController, основан на Permission пересечении: если все ProtectionDomains AccessControlContext, потенциально combined с Principals Subject имеют, статически и/или в соответствии с действующим Policy, Permission проверяются, оценка завершается успешно; в противном случае это не удается.

Subject#doAs в вашем случае не работает, потому что ваш Permission granted к комбинации вашего ProtectionDomain и вашего Principal, но не к самому домену. В частности, во время вызова AccessController#checkPermission(customPermission) эффективный AccessControlContext включал следующие релевантные (с точки зрения оценки Permission) кадры:

Frame # | ProtectionDomain          | Permissions
--------+---------------------------+---------------------------------------------
 2      | "file:./bin/"             | { CustomPermission("someMethod"),
        | + CustomPrincipal("user") |   permissions statically assigned by default
        |                           |   by the ClassLoader }
--------+---------------------------+---------------------------------------------
 1      | "file:./bin/"             | { AuthPermission(
        |                           |   "createLoginContext.MyLoginModule"),
        |                           |   AuthPermission("doAs"), default as above }
--------+---------------------------+---------------------------------------------

Пересечение разрешений этих фреймов, конечно, не включает желаемое CustomPermission.

С другой стороны, Subject#doAsPrivileged, когда задано null AccessControlContext, делает свое дело, потому что "обрезает" стек эффективного контекста до его самого верхнего фрейма, то есть того, из которого вызывается doAsPrivileged. На самом деле происходит следующее: контекст null (пустой) обрабатывается AccessController как если бы это был контекст, оценка разрешений которого дает AllPermission; другими словами:

AllPermissionразрешенияframe2 = { CustomPermission("someMethod"), по умолчанию},

что является (за исключением минимального набора кажущихся посторонними статически назначенными Permnissions) желаемым результатом.

Конечно, в тех случаях, когда такое потенциально произвольное повышение привилегий нежелательно, настраиваемый контекст, чьи инкапсулированные разрешения доменов выражают максимальный набор привилегий, которые вы готовы предоставить (для например, некоторые Subject), могут быть переданы doAsPrivileged вместо null.

Почему реализация Principal вынуждена переопределять equals и hashCode?

Следующий фрагмент трассировки стека иллюстрирует, почему:

at java.lang.Thread.dumpStack(Thread.java:1329)
at com.foo.bar.PrincipalImpl.equals(PrincipalImpl.java:53)
at javax.security.auth.Subject$SecureSet.contains(Subject.java:1201)
at java.util.Collections$SynchronizedCollection.contains(Collections.java:2021)
at java.security.Principal.implies(Principal.java:92)
at sun.security.provider.PolicyFile.addPermissions(PolicyFile.java:1374)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1228)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1191)
at sun.security.provider.PolicyFile.getPermissions(PolicyFile.java:1132)
at sun.security.provider.PolicyFile.implies(PolicyFile.java:1086)
at java.security.ProtectionDomain.implies(ProtectionDomain.java:281)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:450)
at java.security.AccessController.checkPermission(AccessController.java:884)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
...

Дальнейшее чтение:

person Uux    schedule 24.11.2016
comment
Спасибо за исчерпывающий ответ. :-) - person eli; 24.11.2016