Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 24.07.10 15:45
Оценка:
Псевдокод:

AppDomain.CurrentDomain.SetPrincipalPolicy(
    PrincipalPolicy.WindowsPrincipal);
...
// Приложение запущено от имени User0
WindowsIdentity wi1; // User1
WindowsIdentity wi2; // User2

using (var i1 = wi1.Impersonate())
{
    textBox.AppendText(Environment.UserName + "\r\n"); //         <- User1
    textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1
}
textBox.AppendText(Environment.UserName + "\r\n"); //          <- User0
textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!

using (var i2 = wi2.Impersonate())
{
    textBox.AppendText(Environment.UserName + "\r\n"); //         <- User2
    textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!
}
textBox.AppendText(Environment.UserName + "\r\n"); //          <- User0
textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!


Получается, будучи единожды назначенным, Thread.CurrentPrincipal далее уже не зависит от имперсонированного пользователя?! Если к нему обратиться до первой имперсонации, то там окажется User0 и более уже не изменится. Попытки выполнить в любом месте Thread.CurrentPrincipal = null ничего, разумеется, не изменили — значение остаётся тем-же.

Это баг или так задумано? И если последнее, то в чём идея?

PS NET 3.5, завтра проверю на 4-м
"Нормальные герои всегда идут в обход!"
Re: Thread.CurrentPrincipal - непонятно
От: zhech  
Дата: 26.07.10 11:20
Оценка: 5 (1)
Jolly Roger, не пробовали ли Вы такого?) :

JR>Псевдокод:


AppDomain.CurrentDomain.SetPrincipalPolicy(
    PrincipalPolicy.WindowsPrincipal);
...
// Приложение запущено от имени User0
WindowsIdentity wi1; // User1
WindowsIdentity wi2; // User2

using (var i1 = wi1.Impersonate())
{
    textBox.AppendText(Environment.UserName + "\r\n"); //         <- User1
    textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1
    i1.Undo();
}
textBox.AppendText(Environment.UserName + "\r\n"); //          <- User0
textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!

using (var i2 = wi2.Impersonate())
{
    textBox.AppendText(Environment.UserName + "\r\n"); //         <- User2
    textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!
    i2.Undo();
}
textBox.AppendText(Environment.UserName + "\r\n"); //          <- User0
textBox.AppendText(Thread.CurrentPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!
Re: Thread.CurrentPrincipal - непонятно
От: k.o. Россия  
Дата: 26.07.10 13:30
Оценка: 4 (1) +1
Здравствуйте, Jolly Roger, Вы писали:

JR>Псевдокод:


JR>Получается, будучи единожды назначенным, Thread.CurrentPrincipal далее уже не зависит от имперсонированного пользователя?! Если к нему обратиться до первой имперсонации, то там окажется User0 и более уже не изменится. Попытки выполнить в любом месте Thread.CurrentPrincipal = null ничего, разумеется, не изменили — значение остаётся тем-же.


JR>Это баг или так задумано? И если последнее, то в чём идея?


ИМХО, всё так и задумано. Thread.CurrentPrincipal это просто дополнительный, привязанный к потоку, контекст, вообще говоря, не имеющий, ничего общего с тем, под каким пользователем мы работаем. Например, в веб приложении, код будет выполняться от имени одного и того же ASP.NET аккаунта, при этом мы можем использовать Thread.CurrentPrincipal для того, чтобы наше приложение вело себя по разному в зависимости от роли пользователя. Пожалуй, единственная неочевидность в том, что первое чтение Thread.CurrentPrincipal, нужно рассматривать как его инициализацию. Если на время забыть про дополнительные плюшки .NET для Thread.CurrentPrincipal, твой код можно переписать вот так:

...

IPrincipal myGlobalPrincipal = null;

using (var i1 = wi1.Impersonate())
{
    textBox.AppendText(Environment.UserName + "\r\n"); //         <- User1

    // а не инициализировать-ли нам наш глобальный principal?
    var wi = WindowsIdentity.GetCurrent();
    myGlobalPrincipal  = new WindowsPrincipal(wi);

    textBox.AppendText(myGlobalPrincipal.Identity.Name + "\r\n"); // <- User1
}
textBox.AppendText(Environment.UserName + "\r\n"); //          <- User0
textBox.AppendText(myGlobalPrincipal.Identity.Name + "\r\n"); // <- User1 !!!!

...


И теперь ничего удивительного в его поведении нет.
Re[2]: Thread.CurrentPrincipal - непонятно
От: k.o. Россия  
Дата: 26.07.10 13:30
Оценка:
Здравствуйте, zhech, Вы писали:

Z>Jolly Roger, не пробовали ли Вы такого?) :


А Вы пробовали? WindowsImpersonationContext.Dispose вызывает Undo, поэтому, в данном случае, никакого смысла в его явном вызове нет.
Re[3]: Thread.CurrentPrincipal - непонятно
От: zhech  
Дата: 26.07.10 13:35
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Здравствуйте, zhech, Вы писали:


Z>>Jolly Roger, не пробовали ли Вы такого?) :


KO>А Вы пробовали? WindowsImpersonationContext.Dispose вызывает Undo, поэтому, в данном случае, никакого смысла в его явном вызове нет.


Конечно пробовал, при этом все проходило так, как необходимо
Re[3]: Thread.CurrentPrincipal - непонятно
От: _FRED_ Черногория
Дата: 26.07.10 14:54
Оценка:
Здравствуйте, k.o., Вы писали:

Z>>Jolly Roger, не пробовали ли Вы такого?) :


KO>А Вы пробовали? WindowsImpersonationContext.Dispose вызывает Undo, поэтому, в данном случае, никакого смысла в его явном вызове нет.


Во-первых, не лишним было бы почитать документацию:

Notes to Callers
After using Impersonate, it is important to call the Undo method to end the impersonation.

Во-вторых, не лишним было бы проверить С помощью примера кода (раз), рефлектора (два) или исходников (три).

Кстати, очень может быть, что в какой-либо реализации ваши слова и могли бы оказаться правдой, но в таком случае нужно явно указать имеющуюся в виду реализацию — в общепринятых такого нет.
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Thread.CurrentPrincipal - непонятно
От: k.o. Россия  
Дата: 26.07.10 15:32
Оценка: 24 (1)
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, k.o., Вы писали:


Z>>>Jolly Roger, не пробовали ли Вы такого?) :


KO>>А Вы пробовали? WindowsImpersonationContext.Dispose вызывает Undo, поэтому, в данном случае, никакого смысла в его явном вызове нет.


_FR>Во-первых, не лишним было бы почитать документацию:

_FR>

_FR>Notes to Callers
_FR>After using Impersonate, it is important to call the Undo method to end the impersonation.

_FR>Во-вторых, не лишним было бы проверить С помощью примера кода (раз), рефлектора (два) или исходников (три).

раз:
using System;
using System.Linq;
using System.Security.Principal;
using System.Collections.Generic;
using System.Threading;
using System.Runtime.InteropServices;

namespace Test
{
    class Program
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        void Main()
        {
            AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_INTERACTIVE = 2;

            var domain = ".";
            var username = "username";
            var password = "password";

            var tokenHandle = IntPtr.Zero;

            LogonUser(
                username,
                domain,
                password, 
                LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT,
                ref tokenHandle);

            var names = new HashSet<string>();

            var wi = new WindowsIdentity(tokenHandle);
            using (var wic = wi.Impersonate())
            {
                names.Add(Thread.CurrentPrincipal.Identity.Name);
                names.Undo();
            }

            names.Add(Thread.CurrentPrincipal.Identity.Name);

            Console.WriteLine(names.Count); // выводит 1
        }
    }
}


два:

[ComVisible(false)]
public void Dispose()
{
    this.Dispose(true);
}

[ComVisible(false)]
protected virtual void Dispose(bool disposing)
{
    if ((disposing && (this.m_safeTokenHandle != null)) && !this.m_safeTokenHandle.IsClosed)
    {
        this.Undo();
        this.m_safeTokenHandle.Dispose();
    }
}


_FR>Кстати, очень может быть, что в какой-либо реализации ваши слова и могли бы оказаться правдой, но в таком случае нужно явно указать имеющуюся в виду реализацию — в общепринятых такого нет.


версия фреймворка: 3.5.30729.1.
Re[5]: Thread.CurrentPrincipal - непонятно
От: _FRED_ Черногория
Дата: 26.07.10 16:36
Оценка:
Здравствуйте, k.o., Вы писали:

_FR>>Во-вторых, не лишним было бы проверить С помощью примера кода (раз), рефлектора (два) или исходников (три).

KO>раз:

Пример, конечно, не рабочий, но я наконец-то разглядел вызов Undo() в Dispose() Смотрел до этого нескоко раз, не видел
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 26.07.10 16:50
Оценка:
Здравствуйте, zhech, Вы писали:

Z>Jolly Roger, не пробовали ли Вы такого?) :


Z> i1.Undo();


Я пробовал — от безысходности А Вы? Похоже, нет

Дело в том, что Environment.UserName работает честно и без выкрутасов. Она в лоб, что называется, вызывает WinApi функцию GetUserName, а та уж не обманет, проверено годами . Есть у текущего потока токен — она возьмёт имя из него, нет — вернёт имя из токена уровня процесса. И тот факт, что за пределами блока using (var i1 = wi1.Impersonate())
эта функция вернула User0, совершенно однозначно говорит — RevertToSelf был вызван. Ну просто не дано иного И знаете, было-бы крайне, КРАЙНЕ странно, если бы WindowsImpersonationContext.Dispose() не выполняла бы отката.
"Нормальные герои всегда идут в обход!"
Re[3]: Thread.CurrentPrincipal - непонятно
От: _FRED_ Черногория
Дата: 26.07.10 16:55
Оценка: 2 (1)
Здравствуйте, Jolly Roger, Вы писали:

Z>>Jolly Roger, не пробовали ли Вы такого?) :

Z>> i1.Undo();
JR>Я пробовал — от безысходности А Вы? Похоже, нет
JR>Дело в том, что Environment.UserName работает честно и без выкрутасов. Она в лоб, что называется, вызывает WinApi функцию GetUserName, а та уж не обманет, проверено годами . Есть у текущего потока токен — она возьмёт имя из него, нет — вернёт имя из токена уровня процесса. И тот факт, что за пределами блока using (var i1 = wi1.Impersonate())
JR> эта функция вернула User0, совершенно однозначно говорит — RevertToSelf был вызван. Ну просто не дано иного И знаете, было-бы крайне, КРАЙНЕ странно, если бы WindowsImpersonationContext.Dispose() не выполняла бы отката.

Посмотри в отладчике имеющийся контекст имперсонилизавобщемпонятнодолжнобытьяужеязыксломал. Наверняка у него поле m_safeTokenHandle содержит инвалидный хендл. Как и почему — тема отдельного ресёча :о))
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 26.07.10 17:03
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Здравствуйте, Jolly Roger, Вы писали:


KO>ИМХО, всё так и задумано. Thread.CurrentPrincipal это просто дополнительный, привязанный к потоку, контекст, вообще говоря, не имеющий, ничего общего с тем, под каким пользователем мы работаем. Например, в веб приложении, код будет выполняться от имени одного и того же ASP.NET аккаунта, при этом мы можем использовать Thread.CurrentPrincipal для того, чтобы наше приложение вело себя по разному в зависимости от роли пользователя. Пожалуй, единственная неочевидность в том, что первое чтение Thread.CurrentPrincipal, нужно рассматривать как его инициализацию. Если на время забыть про дополнительные плюшки .NET для Thread.CurrentPrincipal, твой код можно переписать вот так:


Как переписать, я в курсе, спасибо Я вобщем-то случайно наткнулся на это поведение — ну вроде удобно, да? Не надо "гонять" Identity между методами, не нужно какие-то списки хранить с риском напутать. Ну конечно, ThreadStatic имеется, но должно-же быть что-то готовое...Да вот-же оно — CurrentPrincipal! И такой обом-с

Но пока до меня высший смысл CurrentPrincipal не дошёл Ладно, "покурю" досуге
"Нормальные герои всегда идут в обход!"
Re[4]: Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 26.07.10 17:26
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Посмотри в отладчике имеющийся контекст имперсонилизавобщемпонятнодолжнобытьяужеязыксломал. Наверняка у него поле m_safeTokenHandle содержит инвалидный хендл. Как и почему — тема отдельного ресёча :о))


Да была, была вызвано Undo — Environment.UserName это однозначно говорит Ну и Undo я пробовал явно вызывать — ноль эффекта, само собой. И токен нормальный — куда он денется, если его никто не закрывает. Проверка-то на его инвалидность как раз и есть контроль, была-ли имперсонация и нужно-ли реверсить. И рефлектором полазил уже — по коду вроде как так и должно быть. Осталось понять — зачем Если на систему не хотели завязывать, то нафига было возвращать текущего при PrincipalPolicy.WindowsPrincipal? А если привязались, то почему не отслеживают? В общем, или я чего-то не понимаю, или они чего-то не додумали / не доделали
"Нормальные герои всегда идут в обход!"
Re[3]: Thread.CurrentPrincipal - непонятно
От: k.o. Россия  
Дата: 26.07.10 17:53
Оценка: +1
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, k.o., Вы писали:


KO>>Здравствуйте, Jolly Roger, Вы писали:


KO>>ИМХО, всё так и задумано. Thread.CurrentPrincipal это просто дополнительный, привязанный к потоку, контекст, вообще говоря, не имеющий, ничего общего с тем, под каким пользователем мы работаем. Например, в веб приложении, код будет выполняться от имени одного и того же ASP.NET аккаунта, при этом мы можем использовать Thread.CurrentPrincipal для того, чтобы наше приложение вело себя по разному в зависимости от роли пользователя. Пожалуй, единственная неочевидность в том, что первое чтение Thread.CurrentPrincipal, нужно рассматривать как его инициализацию. Если на время забыть про дополнительные плюшки .NET для Thread.CurrentPrincipal, твой код можно переписать вот так:


JR>Как переписать, я в курсе, спасибо Я вобщем-то случайно наткнулся на это поведение — ну вроде удобно, да? Не надо "гонять" Identity между методами, не нужно какие-то списки хранить с риском напутать. Ну конечно, ThreadStatic имеется, но должно-же быть что-то готовое...Да вот-же оно — CurrentPrincipal! И такой обом-с


JR>Но пока до меня высший смысл CurrentPrincipal не дошёл Ладно, "покурю" досуге


Ну, так высший смысл в наличии таких штук как, например, http://msdn.microsoft.com/en-us/library/system.security.permissions.principalpermissionattribute.aspx, т.е. в "понимании" рантаймом семантики этого ThreadStatic объекта.
Re[4]: Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 26.07.10 18:25
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Ну, так высший смысл в наличии таких штук как, например, http://msdn.microsoft.com/en-us/library/system.security.permissions.principalpermissionattribute.aspx, т.е. в "понимании" рантаймом семантики этого ThreadStatic объекта.


Хм... Может дело в том, что я с ASP.NET дело не имел, но как-то я не улавливаю связи PrincipalPermissionAttribute говорит: "Чтобы данный код выполнить, у юзера должны быть такие-то права". Это понятно. Но как это коррелирует с CurrentPrincipal? Код не может сам себе назначить произвольные права, его права равны или меньше прав запустившего его пользователя — это постулат системы безопасности. А CurrentPrincipal может назначаться самим кодом. Но даже не это вызывает непонимание — в конце концов Private Object Security в Windows работает на тех-же принципах. Но совершенно не понятно остаётся смысл PrincipalPolicy.WindowsPrincipal с его какой-то недоделанной привязкой к CurrentUser. Ну если Вы взялись отслеживать Windows Principal, так надо-же до конца идти — или вообще его не трогать. По-моему так
"Нормальные герои всегда идут в обход!"
Re[6]: Thread.CurrentPrincipal - непонятно
От: Аноним  
Дата: 26.07.10 20:19
Оценка: -3
_FR>Пример, конечно, не рабочий, но я наконец-то разглядел вызов Undo() в Dispose() Смотрел до этого нескоко раз, не видел

О, эксперт облажался. А как чувствуют себя эксперты, когда садятся в лужу?
Re[7]: Thread.CurrentPrincipal - непонятно
От: _FRED_ Черногория
Дата: 26.07.10 20:20
Оценка: 8 (1) :))) :)
Здравствуйте, Аноним, Вы писали:

_FR>>Пример, конечно, не рабочий, но я наконец-то разглядел вызов Undo() в Dispose() Смотрел до этого нескоко раз, не видел


А>О, эксперт облажался. А как чувствуют себя эксперты, когда садятся в лужу?


Попе мокро.
Help will always be given at Hogwarts to those who ask for it.
Re[5]: Thread.CurrentPrincipal - непонятно
От: k.o. Россия  
Дата: 27.07.10 06:11
Оценка: 2 (1)
Здравствуйте, Jolly Roger, Вы писали:

JR>Здравствуйте, k.o., Вы писали:


KO>>Ну, так высший смысл в наличии таких штук как, например, http://msdn.microsoft.com/en-us/library/system.security.permissions.principalpermissionattribute.aspx, т.е. в "понимании" рантаймом семантики этого ThreadStatic объекта.


JR>Хм... Может дело в том, что я с ASP.NET дело не имел, но как-то я не улавливаю связи PrincipalPermissionAttribute говорит: "Чтобы данный код выполнить, у юзера должны быть такие-то права". Это понятно. Но как это коррелирует с CurrentPrincipal? Код не может сам себе назначить произвольные права, его права равны или меньше прав запустившего его пользователя — это постулат системы безопасности. А CurrentPrincipal может назначаться самим кодом.


Ну, во-первых, CurrentPrincipal может назначаться самим кодом, только если у того есть соответсвующее разрешение.

А во-вторых, это, по-идее, можно использовать для отслеживания прав клиента в серверном приложении. Например, пусть мы хотим разрешить некоторые операции только пользователям, в определённой роли:

public void ProcessFrobnicationRequest()
{
    ...
    AuthenticateUser();
    ...
    Frobnicate(); // allow frobnication only for users in "Gizmo" role.
    ...
}


Можно проверять это самостоятельно:

private void Frobnicate()
{
    var user = Context.CurrentUser;
    if (user.IsInRole("Gizmo"))
    {
        throw new SecurityException();
    }

    ...
}


можно прикрутить AOP (или унаследоваться от CodeAccessSecurityAttribute) и добавлять такие проверки декларативно, с помощью аттрибутов:

[CheckPermission(RequiredRole = "Gizmo")]
private void Frobnicate()
{
    ...
}


в том числе, получая возможность делать это для всех методов класса:
[CheckPermission(RequiredRole = "Gizmo")]
class Frobnicator
{
    ...
}


а можно взять средства предоставляемые FCL:
[PrincipalPermission(SecurityAction.Demand, Role = "Gizmo")]
class Frobnicator
{
    ...
}


Правда, некторые странности в документации PrincipalPermissionAttribute заставляют усомниться в корректности такого использования этого аттрибута с точки зрения разработчиков FCL.

JR>Но даже не это вызывает непонимание — в конце концов Private Object Security в Windows работает на тех-же принципах. Но совершенно не понятно остаётся смысл PrincipalPolicy.WindowsPrincipal с его какой-то недоделанной привязкой к CurrentUser. Ну если Вы взялись отслеживать Windows Principal, так надо-же до конца идти — или вообще его не трогать. По-моему так


Возможно, было бы логичней, сделать явную инициализацию для CurrentPrincipal, но, видимо, текущая реализация происходит из желания упростить его использование для кода с ограниченными правами. Подозреваю, что идея была примерно такая: если у кода есть права на создание WindowsIdentity и, соответсвенно, работу от имени другого пользователя, то у него будут права и на установку CurrentPrincipal и он сможет сам его менять как нужно. В противном случае, не будет прав ни на установку CurrentPrincipal ни на вызов Impersonate, и, соответсвенно, не будет потенциальных проблем с этим связанных.
Re[6]: Thread.CurrentPrincipal - непонятно
От: Jolly Roger  
Дата: 27.07.10 09:30
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Ну, во-первых, CurrentPrincipal может назначаться самим кодом, только если у того есть соответсвующее разрешение.


KO>А во-вторых, это, по-идее, можно использовать для отслеживания прав клиента в серверном приложении. Например, пусть мы хотим разрешить некоторые операции только пользователям, в определённой роли:


Это понятно, это по сути та-же идея, что и Private Object Security, как я уже упоминал. Возможно, в NET немного проще сделано, хотя это как посмотреть.

KO>Возможно, было бы логичней, сделать явную инициализацию для CurrentPrincipal, но, видимо, текущая реализация происходит из желания упростить его использование для кода с ограниченными правами. Подозреваю, что идея была примерно такая: если у кода есть права на создание WindowsIdentity и, соответсвенно, работу от имени другого пользователя, то у него будут права и на установку CurrentPrincipal и он сможет сам его менять как нужно. В противном случае, не будет прав ни на установку CurrentPrincipal ни на вызов Impersonate, и, соответсвенно, не будет потенциальных проблем с этим связанных.


Вот и я про то-же. Либо уж следим за текущим пользователем, либо совсем не трогаем. В принципе, я нашёл, как добиться такого поведения. Надо после каждого вызова Impersonate и Undo выполнять Thread.CurrentPrincipal = null, тогда при следующем обращении он будет создан заново. Конечно, лишний вызов, а с другой стороны — более гибко. Не знаю только, нужна-ли эта гибкость
"Нормальные герои всегда идут в обход!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.