Роли и проекты ASP.net

РЕДАКТИРОВАТЬ - мой исходный вопрос был изменен, чтобы было немного больше информации.


Справочная информация
На работе я работаю над веб-приложением ASP.Net для наших клиентов. В нашей реализации мы используем такие технологии, как проверка подлинности с помощью форм с помощью MembershipProviders и RoleProvider. Все шло хорошо, пока я не столкнулся с некоторыми трудностями при настройке ролей, потому что роли не являются общесистемными, а связаны с учетными записями клиентов и проектами.

Я не могу назвать нашу точную настройку / формулу, потому что я думаю, что наша компания не одобрит это ...

Что такое заказчик / проект?
Наша компания предоставляет управленческую информацию для наших клиентов ежегодно (или с другой периодичностью).
В наших системах заказчик / контракт состоит из:

  • одна учетная запись: информация о компании
  • на Аккаунт, один или несколько Продуктов: набор предоставляемой нами управленческой информации
  • на продукт, одно или несколько измерений: период времени, в течение которого мы собираем и сообщаем данные

Настройка сайта экстрасети
В конечном итоге мы хотим, чтобы все клиенты имели доступ к своей управленческой информации с помощью нашей онлайн-системы. Экстранет состоит из двух сайтов:

  • Сайт компании: предоставляет обзор информации об Учетной записи и Продуктах.
  • Место измерения: после выбора измерения отображается подробная информация за этот период времени.

Сайт измерения - самая интересная часть экстранета. Мы создадим подмодули для новых обзоров, отчетов, управления и поддержки ресурсов, важных для исследования.

Наше решение Visual Studio состоит из нескольких проектов. Одно веб-приложение под названием Portal за основу. Сайты и модули представляют собой виртуальные каталоги в этом приложении (упрощает совместное использование MasterPages среди вещей).

Какие роли?
Следующие пользователи (читай: роли) будут использовать систему:

  • Администраторы: пользователи разработчиков :) (не связанные с клиентами, полный доступ)
  • Сотрудники: сотрудники нашей компании (не связанные с клиентами, полный доступ)
  • Суперпользователь клиента: менеджеры высшего уровня (полный доступ к своей учетной записи / измерениям)
  • Контактное лицо с клиентом: основное контактное лицо (полный доступ к их измерениям)
  • Менеджер по работе с клиентами: руководитель отдела (ограниченный доступ, конкретные данные измерения)

А как насчет пользователей ASP.Net?
В системе будет много пользователей ASP.Net, давайте сосредоточимся на пользователях-клиентах:

  • Пользователи не разделяются между учетными записями
  • SuperUser X автоматически получает доступ ко всем (и новым) измерениям
  • Пользователь Y может быть основным контактным лицом для измерения 1, но не иметь роли для измерения 2.
  • Пользователь Y может быть основным контактным лицом для измерения 1, но иметь роль менеджера для измерения 2.
  • Руководителями отделов являются многие индивидуальные пользователи (для каждого измерения), если бы у менеджера Z был логин для измерения 1, мы хотели бы снова использовать этот логин, если он участвует в измерении 2.

Структура URL
Это типичные URL в нашем приложении:

Мы также создадим URL-адрес документа, по которому вы можете запросить конкретный документ по его GUID. Система должна будет проверить, есть ли у пользователя права на документ. Документ связан с Измерением, Пользователь или определенные роли имеют определенные права на документ.

В чем проблема? (наконец;))
Ролей недостаточно, чтобы определить, что пользователю разрешено просматривать / получать доступ / загружать определенный элемент. Недостаточно сказать, что тот или иной элемент навигации доступен менеджерам. Когда пользователь запрашивает Измерение 1000, мы должны проверить, что у пользователя есть не только роль Менеджера, но и роль Менеджера для Измерения 1000.

Вкратце:

  1. Как мы можем ограничить пользователей их учетными записями / измерениями?
    (помните, что суперпользователи видят все измерения, а некоторые менеджеры - только определенные измерения)

  2. Как мы можем применить роли на уровне продукта / измерения? (пользователь X может быть основным контактом для измерения 1, но только менеджером для измерения 2)

  3. Как мы можем ограничить доступ менеджера к экрану отчетов и только к отчетам их отдела?

И все это с волшебством классов asp.net, возможно, с настраиваемой реализацией поставщика ролей.

Аналогичный вопрос / проблема Stackoverflow
ASP.NET, как управлять пользователями с разными типами ролей


person Zyphrax    schedule 25.02.2010    source источник
comment
@Zyphrax, пожалуйста, пусть вас не смущает большое количество опубликованных сложных и трудоемких обходных путей. Вы можете делать все, что хотите, только с помощью конфигурации. Смотрите мой ответ.   -  person Sky Sanders    schedule 28.02.2010
comment
Я обновил вопрос, чтобы предоставить дополнительную информацию   -  person Zyphrax    schedule 06.03.2010
comment
В этом случае я думаю, что будет достаточно простого подкласса поставщика стандартной роли. Я могу привести пример, но сначала мне нужно, чтобы вы определили, что такое «проект» и как он «переносится». Это очень важно для определения ваших требований.   -  person Sky Sanders    schedule 06.03.2010
comment
@Zyphrax - но позвольте мне просто сказать, что направление, в котором, по-видимому, идет, приведет к системе контроля доступа, которую будет трудно поддерживать с точки зрения как программиста, так и администратора, независимо от того, как вы решите реализовать провайдеров. Было бы намного меньше трений, если бы дизайн был разработан в стиле, совместимом с дизайном поставщиков.   -  person Sky Sanders    schedule 06.03.2010
comment
@Zyphrax - И хотя я нашел этот вопрос достаточно интересным, чтобы уделить ему время и подумать, вам действительно нужно поработать над представлением требований. Фактически невозможно авторитетно прийти к дизайну на основе предоставленной информации. просто говорю...   -  person Sky Sanders    schedule 06.03.2010
comment
@Sky Sanders - спасибо за ваш вклад, сложно описать нашу ситуацию всего в 20 строках текста :), и компания не позволит мне опубликовать полное описание.   -  person Zyphrax    schedule 06.03.2010
comment
@Sky Sanders - мы стараемся максимально придерживаться стандартов / лучших практик ASP.Net. Подводя итог нашей проблеме: стандартная реализация ролей asp.net не подходит. Мы ищем не роли контакта и менеджера, а роли ‹ProjectID› .Contact и ‹ProjectID› .Manager и т. Д. Ролей и т. Д. Контекст учетной записи и проекта пользователя определяет, применяется ли роль.   -  person Zyphrax    schedule 06.03.2010
comment
@Zyphrax - Вы не ограничены в длине вопроса. И я не предлагаю вам изложить свой бизнес-план. Дизайн инфраструктуры, особенно контроль доступа с использованием поставщиков asp, не является глубоким темным секретом, каким бы уникальным ни считал свой проект руководство снежинки. Я просто прошу твердого определения двусмысленных терминов и ожиданий. Я гарантирую вам, что эти «требования» будут немедленно отклонены, если они будут представлены в ситуации rw. Если попросить кого-нибудь потратить время на поиски решения неопределенной проблемы, вероятно, вы не получите нужных результатов.   -  person Sky Sanders    schedule 06.03.2010
comment
@Zyphrax, похоже, вам, ребята, нужен разработчик с опытом проектирования стеков поставщиков .... skysanders.net/subtext /contact.aspx ;-)   -  person Sky Sanders    schedule 06.03.2010
comment
@Sky Sanders - Я постараюсь отредактировать текст вопроса и предоставить немного дополнительной информации. Мы еще не нанимаем вас;), но вся помощь приветствуется.   -  person Zyphrax    schedule 06.03.2010
comment
@Sky Sanders и другие - новый текст вопроса, надеюсь, он дает достаточно информации.   -  person Zyphrax    schedule 07.03.2010
comment
@Zyphrax - это намного лучше, но все еще есть проблемы, критически важные для достижения функционального дизайна, которые неоднозначны. Например, когда вы говорите, что будет много пользователей asp.net, но давайте сосредоточимся на xxxx; Невозможно прийти к методу управления доступом для любого типа пользователя без спецификации для всех типов пользователей.   -  person Sky Sanders    schedule 07.03.2010
comment
@Zyphrax - И описание структуры каталогов мне кажется подозрительным. Виртуальный каталог - это приложение, независимо от того, находится ли оно физически в корневом приложении или нет, и поэтому, если я правильно помню, главные страницы будут вне его vroot, что делает совместное использование невозможным без поставщика виртуального пути, это заставляет меня поверить что «измерения» - это простые подкаталоги корневого приложения. Это сильно повлияет на любую стратегию контроля доступа. В общем - это работа консультанта, а не вопрос StackOverflow. Удачи. Дайте мне знать, если я смогу помочь.   -  person Sky Sanders    schedule 07.03.2010


Ответы (4)


В различных сообщениях, которые я вижу, вы ищете механизм настраиваемых ролей или, по-другому, настраиваемый механизм авторизации. Для аутентификации по-прежнему можно использовать стандартный SqlMembershipProvider.

Я не уверен, что стандартный поставщик ролей предоставит вам то, что вы хотите, поскольку для авторизации требуется наличие контекста проекта. Однако вы можете изучить вопрос о написании настраиваемого RoleProvider, чтобы узнать, можете ли вы создать некоторые настраиваемые методы, которые будут делать это. Тем не менее, чтобы ответить на этот вопрос, я предполагаю, что вы не можете использовать SqlRoleProvider.

Итак, вот некоторая потенциальная схема:

Create Table Companies
(
    Id int not null Primary Key
    , ...
)
Create Table Projects
(
    Id int not null Primary Key
    , PrimaryContactUserId uniqueidentifier
    , ...
    , Constraint FK_Projects_aspnet_Users
        Foreign Key ( PrimaryContactUserId )
        References dbo.aspnet_Users ( UserId )
)
Create Table Roles
(
    Name nvarchar(100) not null Primary Key
    , ...
)

Create Table ProjectCompanyRoles
(
    CompanyId int not null
    , ProjectId int not null
    , RoleName nvarchar(100) not null
    , Constraint FK_...
)

Как я уже сказал, причина включения PrimaryContact в таблицу Projects состоит в том, чтобы гарантировать, что для данного проекта существует только один. Если вы включите его в качестве роли, вам придется включить кучу кода прыжка через обруч, чтобы гарантировать, что проекту не назначено более одного PrimaryContact. Если это так, возьмите PrimaryContactUserId из таблицы Projects и сделайте его ролью.

Проверки авторизации повлекут за собой запросы к ProjectCompanyRoles. Опять же, добавление контекстов Project и Company затрудняет использование поставщиков ролей по умолчанию. Если вы хотите использовать механизм .NET для ролей, а также для аутентификации, вам придется реализовать свой собственный RoleProvider.

person Thomas    schedule 28.02.2010
comment
Вы подразумеваете соотношение 1: 1 между пользователем и проектом, что не так. У проекта много пользователей, один из которых является основным контактным лицом, однако другие пользователи могут иметь роли admin, edit, x, y, z для этого конкретного проекта. - person Zyphrax; 28.02.2010
comment
Действительно ли у каждого проекта должно быть одно и только одно основное контактное лицо? Вышеупомянутая структура не препятствует управлению другими ролями в отношении данного проекта; он просто предоставляет средства для обеспечения того, чтобы у каждого проекта было основное контактное лицо, и, помимо структуры ролей, может предоставить кому-то особые разрешения. - person Thomas; 28.02.2010
comment
Я думаю, вы слишком много думаете об этом. Проблема, на мой взгляд, заключается в том, чтобы просто предоставить централизованное членство и отдельные разрозненные роли. - person Sky Sanders; 28.02.2010

Это именно тот сценарий, который требует настраиваемого RoleProvider. Вы разрабатываете схему базы данных для поддержки своего случая (вы можете создать таблицу с именем ProjectRole и таблицу с именем CompanyRole).

Вот несколько советов для начала (со ссылками на помощь внизу):

Добавьте этот раздел в свой web.config:

<roleManager defaultProvider="MyRoleProvider" enabled="true">
    <providers>
        <add name="MyRoleProvider" type="MyNamespace.MyRoleProvider, MyAssembly, Version=1.0.0.0" description="My Custom Role Provider." enableSearchMethods="false" applicationName="MyApplicationName"/>
    </providers>
</roleManager>

Тогда вот как выглядит (более или менее) класс MyRoleProvider:

(ПРИМЕЧАНИЕ: ваш класс должен наследовать от System.Web.Security.RoleProvider)

namespace MyNamespace
{
    ...

    public class MyRoleProvider : System.Web.Security.RoleProvider
    {
        private string _applicationName;

        public MyRoleProvider()
        {
        }

        public override string ApplicationName
        {
            get
            {
                return _applicationName;
            }
            set
            {
                _applicationName = value;
            }
        }

        ...

    }
}

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

Как минимум, я бы переопределил эти 2 метода:

  • GetRolesForUser
  • IsUserInRole

Но вы также можете переопределить эти методы, если хотите:

  • AddUsersToRoles
  • RemoveUsersFromRoles
  • FindUsersInRole
  • GetUsersInRole
  • GetAllRoles
  • CreateRole
  • DeleteRole
  • RoleExists

И вот ссылки, которые я обещал:

person Gabriel McAdams    schedule 28.02.2010

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: В соответствии с обменом в комментариях, в котором я полностью описываю себя, было найдено почти готовое решение, и этот ответ был очищен от всякой неразберихи и теперь содержит только протестированный сценарий, который может решить или не решить проблему OP. ;-)

Престижность Томаса за то, что он сохранил хладнокровие и не сдался.


Z- скажите, понимаю ли я вас:

Вам нужен центральный поставщик членства для всех приложений / проектов и отдельный бункер ролей для каждого приложения / проекта?

Возможно, вам не потребуется реализовывать настраиваемые поставщики. Стандартного стека может хватить с незначительной модификацией хранимой процедуры. Всегда лучше попробовать и сладко поговорить с привычными системами, чтобы делать то, что вы хотите. Это приводит к меньшему количеству работы и большему количеству сна.

Основные аспекты предлагаемого решения:

  • Общая база данных и строка подключения,
  • Общее имя заявки на членство,
  • Общий раздел machineKey, чтобы каждый сайт использовал билет общих форм.
  • Имя приложения поставщика роли UNIQUE (или, как вы говорите, projectId).
  • Модифицированный aspnet_Users_DeleteUser sproc.

Модификация aspnet_Users_DeleteUser включает в себя очистку пользовательских ссылок в aspnet_users, которые динамически создаются поставщиками ролей и профилей и содержат условие, что конкретный экземпляр aspnet_db принадлежит общему MembershipProvider, и только сайты, которые используйте тот общий поставщик членства, который должен подключиться к нему.

Чтобы сопоставить это решение со сценарием OP:

Каждая учетная запись / компания будет иметь отдельный экземпляр aspnet_db, а «ProjectId» будет сопоставлен с атрибутом applicationName элемента поставщика RoleManager.

По мере того, как проекты «переносятся», им назначается новый ProjectId (applicationName), и при этом пользователи компаний могут аутентифицироваться в перенесенном проекте с помощью общего поставщика членства, но роли из исходного проекта не переносятся в силу разные поставщики ролей.

Все стандартные стратегии управления членством, например Инструмент настройки AspNet, элементы управления входом, мастера создания пользователей, функции членства (особенно Membership.DeleteUser () - спасибо, Томас) будут вести себя так, как ожидалось, без каких-либо изменений.

Профили могут быть реализованы в любом направлении, использование applicationId поставщика членства позволит данным профиля следовать за пользователем в любой из связанных проектов. Использование отдельного ProjectId (applicationName) поставщика ролей позволит создать отдельные профили для каждого пользователя в каждом проекте.

Более подробная информация и тесты: здесь.

Основные разделы конфигурации перечислены ниже, а затем измененный sproc.

Web.config

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="testDb" providerName="System.Data.SqlClient" connectionString="Data Source=(local);Initial Catalog=__SingleAuthMultiRole;Integrated Security=True"/>
  </connectionStrings>
  <system.web>
    <compilation debug="true"/>

    <!-- this key is common all your apps - generate a new one @ http://www.developmentnow.com/articles/machinekey_generator.aspx -->
    <machineKey validationKey="841FEF8E55CD7963CE9EAFED329724667D62F4412F635815DFDDBE7D2D8D15819AE0FDF70CEF8F72792DBD7BF661F163B01134092CBCB80D7D71EAA42DFBF0A9" decryptionKey="FC9B0626224B0CF0DA68C558577F3E37723BB09AACE795498C4069A490690669" validation="SHA1" decryption="AES"/>

    <authorization>
      <deny users="?"/>
    </authorization>

    <authentication mode="Forms" />

    <membership defaultProvider="SqlProvider" userIsOnlineTimeWindow="15">
      <providers>
        <clear/>
        <add name="SqlProvider"
             type="System.Web.Security.SqlMembershipProvider"
             connectionStringName="testDb"
             applicationName="Common"  /> <!-- membership applicationName is common to all projects  -->
      </providers>
    </membership>

    <roleManager enabled="true" defaultProvider="SqlRoleManager" cacheRolesInCookie="true">
      <providers>
        <add name="SqlRoleManager"
             type="System.Web.Security.SqlRoleProvider"
             connectionStringName="testDb"
             applicationName="WebApplication1"/> <!-- roleManager applicationName is unique to each projects  -->
      </providers>
    </roleManager>

  </system.web>
</configuration>

Использование: после подготовки Aspnet_db с помощью aspnet_regsql.exe запустите этот сценарий, чтобы изменить sproc aspnet_Users_DeleteUser.

/*************************************************************/
/*************************************************************/
--- Modified DeleteUser SP

IF (EXISTS (SELECT name
              FROM sysobjects
             WHERE (name = N'aspnet_Users_DeleteUser')
               AND (type = 'P')))
DROP PROCEDURE [dbo].aspnet_Users_DeleteUser
GO
CREATE PROCEDURE [dbo].[aspnet_Users_DeleteUser]
    @ApplicationName  nvarchar(256),
    @UserName         nvarchar(256),
    @TablesToDeleteFrom int,
    @NumTablesDeletedFrom int OUTPUT    

AS
BEGIN
    -- holds all user id for username
    DECLARE @UserIds TABLE(UserId UNIQUEIDENTIFIER)
    SELECT  @NumTablesDeletedFrom = 0

    DECLARE @TranStarted   bit
    SET @TranStarted = 0

    IF( @@TRANCOUNT = 0 )
    BEGIN
        BEGIN TRANSACTION
        SET @TranStarted = 1
    END
    ELSE
    SET @TranStarted = 0

    DECLARE @ErrorCode   int
    DECLARE @RowCount    int

    SET @ErrorCode = 0
    SET @RowCount  = 0

    -- get all userid for username
    INSERT INTO @UserIds
    SELECT  UserId
    FROM    dbo.aspnet_Users 
    WHERE   LoweredUserName = LOWER(@UserName)

DECLARE @tmp int
SELECT @tmp = COUNT(*) FROM @UserIds
    IF NOT EXISTS(SELECT * FROM @UserIds)
        GOTO Cleanup

    -- Delete from Membership table if (@TablesToDeleteFrom & 1) is set
    IF ((@TablesToDeleteFrom & 1) <> 0 AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_MembershipUsers') AND (type = 'V'))))
    BEGIN
        DELETE FROM dbo.aspnet_Membership WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
               @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_UsersInRoles table if (@TablesToDeleteFrom & 2) is set
    IF ((@TablesToDeleteFrom & 2) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_UsersInRoles') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_UsersInRoles WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_Profile table if (@TablesToDeleteFrom & 4) is set
    IF ((@TablesToDeleteFrom & 4) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_Profiles') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_Profile WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_PersonalizationPerUser table if (@TablesToDeleteFrom & 8) is set
    IF ((@TablesToDeleteFrom & 8) <> 0  AND
        (EXISTS (SELECT name FROM sysobjects WHERE (name = N'vw_aspnet_WebPartState_User') AND (type = 'V'))) )
    BEGIN
        DELETE FROM dbo.aspnet_PersonalizationPerUser WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    -- Delete from aspnet_Users table if (@TablesToDeleteFrom & 1,2,4 & 8) are all set
    IF ((@TablesToDeleteFrom & 1) <> 0 AND
        (@TablesToDeleteFrom & 2) <> 0 AND
        (@TablesToDeleteFrom & 4) <> 0 AND
        (@TablesToDeleteFrom & 8) <> 0 AND
        (EXISTS (SELECT UserId FROM dbo.aspnet_Users WHERE UserId IN (SELECT UserId from @UserIds))))
    BEGIN
        DELETE FROM dbo.aspnet_Users WHERE UserId IN (SELECT UserId from @UserIds)

        SELECT @ErrorCode = @@ERROR,
                @RowCount = @@ROWCOUNT

        IF( @ErrorCode <> 0 )
            GOTO Cleanup

        IF (@RowCount <> 0)
            SELECT  @NumTablesDeletedFrom = @NumTablesDeletedFrom + 1
    END

    IF( @TranStarted = 1 )
    BEGIN
        SET @TranStarted = 0
        COMMIT TRANSACTION
    END

    RETURN 0

Cleanup:
    SET @NumTablesDeletedFrom = 0

    IF( @TranStarted = 1 )
    BEGIN
        SET @TranStarted = 0
        ROLLBACK TRANSACTION
    END

    RETURN @ErrorCode

END
GO
person Sky Sanders    schedule 28.02.2010
comment
Спасибо, но есть много проектов, а не приложений. Я хотел бы определить роли для каждого ProjectID - person Zyphrax; 01.03.2010
comment
Похоже, это должно работать, но на практике это не сработает. Например, FindUsersInRole, GetUsersInRole будет возвращать только пользователей, связанных с приложением SqlRoleProvider. Таким образом, если ваши центральные пользователи связаны с другим приложением, вы не получите никаких возвратов. Чтобы это сработало, я обнаружил, что мне нужно написать собственный RoleProvider, который привязан к ApplicationName в SqlMembershipProvider. - person Thomas; 03.03.2010
comment
-1: Это взлом. Найдите время, чтобы написать его правильно, и вы не обожжетесь позже, когда вам понадобится внести какие-то непредвиденные изменения. - person Gabriel McAdams; 03.03.2010
comment
@ Габриэль, не могли бы вы уточнить? Я не уверен, как это можно охарактеризовать как взлом, я просто настраиваю существующие системы для предоставления требований в виде состояния. @Thomas, заявленные требования, как я их читал, требуют поведения, которого вы пишете собственный код, чтобы избежать .. - person Sky Sanders; 03.03.2010
comment
@Sky: Хакерство - вы берете систему, которая была разработана для одного дела, и используете ее для чего-то другого. Нет причин делать то, что вы предлагаете, потому что существует система, которая делает именно то, что нужно OP (поставщик настраиваемых ролей). - person Gabriel McAdams; 03.03.2010
comment
@ Габриэль. Два момента: я использую систему ТОЧНО, как она была разработана для использования. Сделайте шаг назад и покажите мне, где, за исключением вашего мнения, такое использование (тем не менее, без кода) описывается как неприемлемое. И скажите, пожалуйста, какой аспект моего предложения является взломом. Во-вторых: поставщик настраиваемых ролей не «существует», вы должны разработать, запрограммировать и поддерживать настраиваемого поставщика. Я думаю, что вы неправильно понимаете требования ОП и характеризуете мое предложение как взлом, выявляет дефект восприятия. просто говорю... - person Sky Sanders; 03.03.2010
comment
Предоставленная вами структура на практике работать не будет! Когда вы запрашиваете SqlRoleProvider каждого приложения, он будет использовать атрибут ApplicationName поставщика ролей, а не ApplicationName SqlMembershipProvider. Вы не сможете запрашивать пользователей в ролях, например, используя предоставленную вами схему. Поэтому, когда вы создаете пользовательский BOB для приложения Common, вы не сможете найти роли для BOB с помощью RoleProvider, для которого ApplicationName установлено значение WebApp2. Я лично столкнулся с этой проблемой, и мне пришлось написать собственный RoleProvider. - person Thomas; 03.03.2010
comment
@Sky: Атрибут ApplicationName предназначен для обозначения applications, а не чего-то еще, как вы предлагаете. Я никогда не говорил, что ваше предложение неприемлемо. Я сказал, что это хитрость (обход дизайна для достижения цели). ASP.Net позволяет разработчикам писать собственные поставщики из-за подобных ситуаций. - person Gabriel McAdams; 03.03.2010
comment
@ Габриэль, что еще я предлагаю? Вы не видите 3 web.configs? это означает 3 приложения, следовательно, 3 названия приложений. - person Sky Sanders; 03.03.2010
comment
@ALL - Я думаю, что здесь происходит ОГРОМНОЕ отключение из-за некоторой двусмысленности в вопросе OP. Я читал: у него есть несколько «проектов», будь то приложения или просто аспекты одного приложения, в которых определенные пользователи должны иметь роли. Эти проекты предназначены для повторного развертывания, и при повторном развертывании назначения ролей должны быть специфичными для экземпляра экстрасети, а не для источника / промежуточного уровня - и все это при сохранении центрального хранилища аутентификации. Это то, что я прочитал, и это проблема, которую мое предложение решает без кодирования, используя фреймворк, как задумано. - person Sky Sanders; 03.03.2010
comment
@Небо. Не существует решения, использующего встроенные классы, которое позволяло бы иметь несколько приложений с независимыми ролями с одним хранилищем проверки подлинности с использованием проверки подлинности с помощью форм. Этого не существует. Вы должны написать собственный RoleProvider. SqlMembershipProvider и SqlRoleProvider таким образом работать не будут. В тот момент, когда вы используете SqlRoleProvider для получения ролей, вы будете использовать ApplicationName SqlRoleProvider, а пользователи, привязанные к ApplicationName SqlMembershipProvider, будут проигнорированы. - person Thomas; 03.03.2010
comment
@Sky: теперь я думаю, что понимаю разобщенность. OP не говорит о проекте так, как вы. project в этом смысле - это не часть приложения, а обозначение внутри приложения. Думайте об этом так, как если бы он назывался location - person Gabriel McAdams; 03.03.2010
comment
@Thomas - ТОЧНО! прочтите то, что вы только что написали. Это именно то, что заставляет это решение работать OOB. И вы явно ошибаетесь, утверждая, что UsersInRole не вернет ожидаемых результатов. Есть ссылка на какой-то код, опровергающий это утверждение. просто добавьте var users = Roles.GetUsersInRole("webApp3Role"); в page_load по умолчанию app3, чтобы понять, о чем я говорю. - person Sky Sanders; 03.03.2010
comment
@ Габриэль, в любом случае, это все еще работает. Я использую термин «проект» неоднозначно, поскольку я не могу определить восприятие / реализацию «проекта» OP. Я чувствую себя в безопасности, потому что в любом из сценариев, в каком-либо месте или в приложении решение работает идеально, как и было задумано. ;-) - person Sky Sanders; 03.03.2010
comment
@Gabriel, Dude, Roles не имеет возможности узнать guid пользователя, не знает провайдера членства и никак не взаимодействует с провайдером членства / таблицами / ничем. Он просто берет ИМЯ ПОЛЬЗОВАТЕЛЯ участника потока. Я говорю вам, что это работает. Я предоставил рабочее решение, чтобы доказать это. Можем ли мы втроем просто взглянуть на материальные ценности здесь? - person Sky Sanders; 03.03.2010
comment
@Sky: согласен. Вы предоставили рабочее решение. На мой взгляд, это не лучшее решение. Я изложил свои причины для этого. - person Gabriel McAdams; 03.03.2010
comment
@All, я мог бы найти время, чтобы повторно реализовать данное решение, используя сценарий на основе «местоположения», и выгрузить пользователей / роли на каждой странице по умолчанию, но это будет позолочить лилию. Если эта концепция все еще сбивает с толку, необходимо решить другие проблемы. И позвольте мне просто прикрыть свою задницу и сказать, что если я что-то не понимаю, мне очень жаль, но я почти уверен, что понимаю требования, и необходимость в настраиваемом провайдере не указывается. - person Sky Sanders; 03.03.2010
comment
@Gabriel: Я понимаю, что вы возражаете против того, что я разрушаю стек провайдеров. Это правильно? - person Sky Sanders; 03.03.2010
comment
@ Sky-Ты ошибаешься вдвойне. ЛЮБОЙ вызов RP будет использовать AppName RP. Roles.GetUsersInRole будет использовать RP, который снова будет использовать AppName RP, а не AppName MP! Вот доказательство: в этой системе AppId пользователя будет отличаться от AppId роли, верно? Обратите внимание, что хранимая процедура aspnet_UsersInRoles_GetRolesForUser принимает только один параметр для AppName? Как он может возвращать пользователей из одного приложения и роли из другого приложения только с одним параметром? Не может. Он использует только одно AppName, и это имя приложения для RoleProvider. - person Thomas; 03.03.2010
comment
@Thomas - мы по-прежнему не видим проблему таким же образом. Я прочитал, что OP хочет, чтобы роли были специфичными для экземпляра приложения, но аутентификация была общей для всех. Можем ли мы с этим согласиться? - person Sky Sanders; 03.03.2010
comment
@ Томас, И я не говорил, что ты ошибаешься. Я заявил, что ваше описание поведения провайдеров абсолютно верно. Но именно ваше восприятие предметной области заставляет вас воспринимать такое поведение как дефектное. Я утверждаю, что это желаемое поведение. - person Sky Sanders; 03.03.2010
comment
@Thomas, у ПОЛЬЗОВАТЕЛЯ НЕТ приложения. У пользователя есть ИМЯ ПОЛЬЗОВАТЕЛЯ. У провайдеров есть AppId. Это то, что делает решение соответствующим требованиям. Стек поставщика был разработан так, чтобы быть абстрактным, поскольку каждому компоненту не нужно знать детали реализации другого. Общий API - это строка UserName, которая является общей для поставщика аутентификации. Пользователь не может получить доступ к сайту без аутентификации, что означает, что роли, определенные с другим appId на RP, будут связаны с надлежащим пользователем. - person Sky Sanders; 03.03.2010
comment
@Thomas - суть в том, что ПОЛЬЗОВАТЕЛЬ не состоит в браке ни с одним провайдером, кроме МП. Это задача MP - гарантировать, что текущий принципал аутентифицирован. Единственный член принципала, который используется в RP или PP, - это Name. И OP хочет знать, играет ли текущий пользователь роль в текущем приложении, И хочет использовать одно хранилище аутентификации. - person Sky Sanders; 03.03.2010
comment
@Gabriel: Вы проголосовали против рабочего решения, назвали его взломом и заявили, что стек поставщика не предназначен для использования таким образом. Есть ли у вас что-нибудь, кроме вашего мнения, в поддержку этих утверждений? Я написал много полных стеков провайдеров, объединив невероятно разнообразный диапазон информационных хранилищ, и понимаю необходимость реализации индивидуальных провайдеров. Я также узнал, что если стек провайдера может обеспечить необходимую вам функциональность только через конфигурацию, то это ВСЕГДА ваш лучший выбор. - person Sky Sanders; 03.03.2010
comment
@Sky: Я согласен с тем, что идея состоит в том, чтобы иметь центральное хранилище аутентификации с разными списками ролей для каждого приложения. Я создал что-то, что делает именно это, и первое, что я попробовал, - это использовать поставщиков Sql так, как вы описываете. Это не работает, потому что, когда приложения делают вызовы для получения списка пользователей в ролях или наоборот, они предполагают, что все пользователи имеют то же приложение, что и роль. - person Thomas; 04.03.2010
comment
@Sky: RE: Bottom Line Пользователь и роли связаны с определенным AppId / AppName. Когда вы запрашиваете MP, он будет использовать AppName MP. Когда вы запрашиваете у RP, он будет использовать AppName RP. Проблема в том, что все, что требует пересечения границ, не сработает. Итак, Roles.GetUsersInRole переводится как Использование RP для привязки всех пользователей к AppName RP, которые принадлежат роли с name = X и AppName этой роли = AppName RP. - person Thomas; 04.03.2010
comment
@Thomas - снова то, что вы говорите, правильно. Вы не понимаете, что требования OP заключаются в разделении ролей, а не в агрегировании ролей. Однако, когда новый проект экспортируется в экстранет, он больше не может быть основным контактным лицом. И потеряете эту роль для Аккаунта и нового проекта. Тем не менее, эта роль по-прежнему остается для первого проекта ». - например, когда «проект», что бы это ни значило, переносится, назначается новый appid и назначаются соответствующие роли. МП не меняется и не требует дополнительной настройки. - person Sky Sanders; 04.03.2010
comment
@Sky, я думаю, мы говорим о яблоках и апельсинах. Я полностью согласен с тем, что спецификация ОП расплывчата. Я подозреваю, что Project! = Application, как говорили другие. Тем не менее, если вам нужно несколько приложений с независимыми, раздельными ролями и одним хранилищем аутентификации, опубликованная вами конфигурация не будет работать (если, конечно, вы никогда не привяжете пользователей к ролям). - person Thomas; 04.03.2010
comment
@ Томас, теперь мы кое-что получим. Мы договорились о проблемной области. Независимо от того, является ли «проект» приложением или нет (это техническая семантика, которая в данном сценарии не обсуждается), я полностью утверждаю, что опубликованное мной решение может работать, а работает работают, чтобы обеспечить центральную аутентификацию и отдельные разрозненные роли. Связанное решение имеет общего пользователя в конкретной роли для каждого приложения. Войдите в одну, вы во все вошли. В каком бы приложении вы ни находились, применяются определенные роли. Вы смотрели демо или основываете свое мнение на прошлом опыте? - person Sky Sanders; 04.03.2010
comment
@Thomas - для перспективы в связанном решении подумайте о WebApp1 как о внутреннем «проекте», а WebApp2, WebApp3 как о экземплярах WebApp1 во внешней сети. Кстати, UID: CommonUser, PWD: CommonUser! - person Sky Sanders; 04.03.2010
comment
@Небо. Хуже того, чем не работать, это будет выглядеть так, как будто это работает. (из WebApp1): var user = Membership.CreateUser (test, 12345, [email protected]); Roles.CreateRole (TestRole); // эта строка создает вторую запись пользователя с другим AppId! Roles.AddUserToRole (тест, TestRole); user = Membership.GetUser (тест); Membership.DeleteUser (тест); var users = Roles.GetUsersInRole (TestRole); Debug.Assert (users.Length == 0); - person Thomas; 04.03.2010
comment
@Sky Я понимаю структуру. Как я уже сказал, я создал что-то именно для этого, и мне пришлось написать собственный RoleProvider, чтобы обойти тот факт, что, если AppName MP и AppName RP различны, это создает всевозможные проблемы. - person Thomas; 04.03.2010
comment
Кстати, строка, которая создает второго пользователя, - это Roles.AddUserToRole. По сути, RP говорит: «Привет, у меня нет пользователя, поэтому я создам новый с моим AppId, а затем перенаправлю этого пользователя в свою роль». - person Thomas; 04.03.2010
comment
@Thomas, я не могу объяснить сильные стороны и тонкие странности стека провайдеров в комментариях. См. Обновленный ответ. Если вы хотите обсудить это дальше, отправьте мне отзыв из моего блога, чтобы я мог получить ваш адрес электронной почты и отключить его. Мир. - person Sky Sanders; 04.03.2010
comment
@Sky Мы, конечно, можем. Я постараюсь связаться с вами позже на этой неделе. Я хорошо разбираюсь в стеке поставщиков и особенно в SqlProvider, который я использую в производстве. Я долго разбирался с его странностями. В конце концов, MS разработала SqlProviders (MP и RP), чтобы у вас было несколько приложений, каждое со своим собственным списком пользователей и ролей. Если вы посмотрите на код, который он использует, становится ясно, что он никогда не был предназначен для обработки нескольких приложений с их собственными ролями, а только с одним хранилищем аутентификации. - person Thomas; 04.03.2010
comment
@all - я призвал большие пушки по этому поводу. Давайте подождем и посмотрим, как упадут фишки. - person Sky Sanders; 04.03.2010
comment
@Thomas, сначала вы громко, несколько раз заявляете, что (рабочее) решение, которое я представил, не будет работать, затем, используя ошибочный тест с использованием неправильной процедуры в качестве поддержки, громко заявляйте, что оно не работает, и теперь, когда будет доказано, что это действительно работает, вы полагаетесь на личное мнение, что это не было разработано таким образом. В целом, несмотря на эмпирические данные, он работает нормально. Становится совершенно очевидным, что ваша позиция для вас более ценна, чем факты. Я ничего не могу сделать здесь, кроме как указать вам на bobsutton.typepad.com/ my_weblog / 2006/07 / strong_opinions.html - person Sky Sanders; 04.03.2010
comment
@Thomas и codinghorror.com/blog/2008/ 05 / strong-views-weakly-hold.html мир. - person Sky Sanders; 04.03.2010
comment
На самом деле, это не работает, и предоставлен тестовый код, чтобы доказать это! - person Thomas; 05.03.2010
comment
Как я уже говорил много раз, любой код, вызываемый против поставщика ролей, будет использовать имя приложения поставщика ролей. Что еще хуже, SqlRoleProvider создаст пользователя с другим AppId. Просмотрите пример, который я привожу для вашего решения, кстати. Вы сделаете это до того, как позвоните в Membership.DeleteUser, у вас будет ДВА пользователя и одна роль. Я никогда не называл себя экспертом, но я имел дело с этой конкретной проблемой (и я останавливался в Holiday Inn вчера вечером). - person Thomas; 05.03.2010
comment
Хуже того, чем не работать, это будет выглядеть так, как будто это работает. (из WebApp1): ‹pre› ‹code› var user = Membership.CreateUser (test, 12345, [email protected]); Roles.CreateRole (TestRole); // эта строка создает вторую запись пользователя с другим AppId! Roles.AddUserToRole (тест, TestRole); user = Membership.GetUser (тест); Membership.DeleteUser (тест); var users = Roles.GetUsersInRole (TestRole); Debug.Assert (users.Length == 0); ‹/code› ‹/pre› - person Thomas; 05.03.2010
comment
Дерьмо. Не могу форматировать в комментариях. В любом случае, если вы запустите этот код, вы получите ошибку в утверждении, потому что ваша роль будет по-прежнему думать, что пользователь является частью тестовой роли. - person Thomas; 05.03.2010
comment
Между прочим, позвольте мне добавить, что если вы хотите предоставить пользователям возможность изменять свое имя пользователя, которое предоставляется не как часть SqlMembershipProvider, но обычно реализуется, и вы используете свою структуру, это полностью уничтожит все ваши результаты. Если я изменю имя пользователя BOB на ROBERT, вызовы GetUsersInRole вернут имя пользователя, которого не существует. - person Thomas; 05.03.2010
comment
@Thomas - Вы правы, говоря, что это решение не работает из коробки. Я заметил, что становлюсь горячим, сосредотачиваясь исключительно на некоторых, на мой взгляд, несоответствиях в логике ваших возражений, и сделал шаг назад, написал кучу тестов и обнаружил, что действительно MP.DeleteUser, даже когда я использовал RP для удаления пользователя из ролей, все еще оставался беспорядок для других приложений, которые могли иметь роли, определенные для удаленного пользователя. Все еще придерживаясь своей позиции, я сделал простой мод для sproc aspnet_Users_DeleteUser, который решает все эти проблемы. И тут меня осенило ... - person Sky Sanders; 05.03.2010
comment
@ Томас, какой я был засранцем. Я хочу извиниться, если можно, и еще раз изложу свою позицию. Но прежде чем я это сделаю, мне нужно прояснить кое-что, что действительно есть. Записи в aspnet_users не являются пользователями, они являются именами пользователей. RP не создает пользователей, она просто берет ваше слово, когда вы говорите, что имя пользователя является действительным, и создает строку «пользователь», имя пользователя, если хотите, для собственного использования, если она не находит ее. На него нужно что-то вешать. Вот и все. Если беспорядок, оставленный удалением пользователя, позаботится о сценарии, золотой. Еще не из коробки, но золотой. - person Sky Sanders; 05.03.2010
comment
@Thomas: - Все, что связано с членством, по-прежнему распределено между строкой общего пользователя aspnet_Membership и строкой общего пользователя aspnet_Users. Роли, профили и т. Д. Не заботятся об этих свойствах. Итак, в этом сценарии, помимо удаления, эти несколько строк aspnet_user - это то, что заставляет это решение работать. Итак - если интересно, в ответ добавляется изменение sproc с извинениями. Я по-прежнему считаю, что незначительное изменение sproc намного проще, чище и легче в обслуживании (без обслуживания), чем написание, тестирование и поддержка нового стека поставщиков. Я сказал это. Мир. - person Sky Sanders; 05.03.2010
comment
@Thomas - более подробную информацию и тесты можно найти здесь: skysanders.net/subtext/archive/2010/03/05/, я думаю, вам будет интересно. - person Sky Sanders; 05.03.2010
comment
Привет, небо. Я разместил комментарий к ссылке в вашем блоге. Приятно знать, что я не сумасшедший: D. Проверьте вариант использования изменения имени пользователя. Это тоже оставляет беспорядок. В конце концов я обнаружил, что мне нужно написать настраиваемого поставщика ролей. - person Thomas; 05.03.2010
comment
@thomas, конечно - такая внешняя функция может потребовать немного больше работы. Короткая спираль подойдет лучше всего. Никаких изменений в стеке стандартного поставщика не требуется. Следующее возражение? ржу не могу. - person Sky Sanders; 05.03.2010
comment
Да, вы можете изменить сохраненные процессы, чтобы исправить проблему. Я чувствовал, что это проблематично. Если бы я только изменил sp, для кого-то, смотрящего на схему или файлы конфигурации, не было бы ясно, что я их изменил. На самом деле, это выглядело бы так, как будто я использовал готовую функциональность. Я создал отдельный RoleProvider и отдельные процессы, чтобы прояснить как в конфигурации, так и в базе данных, что я отклонился от встроенной функциональности. - person Thomas; 05.03.2010
comment
@ Томас - Я действительно не знаю, как на это ответить. Итак, вы говорите, что почувствовали необходимость заново реализовать рабочий код и sprocs, чтобы смягчить какой-то кризис неоднозначности? Разве не хватит смелого предисловия к документам или прочтения? Кто вас беспокоит, что вас запутают? Если я уравновешиваю эту озабоченность с преимуществами незначительного изменения (изменено 8 строк) в стандартном sproc и sproc изменения имени пользователя вне диапазона из 4 строк, я бы склонялся к отказу от компилированного и развернутого кода с незначительными изменениями sproc и правильным документы - person Sky Sanders; 05.03.2010
comment
У меня было несколько проблем с рассматриваемой системой. Во-первых, я хотел убедиться, что, когда я передаю систему другим разработчикам, через много лет будет ясно, что над поставщиком была проделана некоторая индивидуальная работа, и это не было нестандартным решением. -коробочное решение. Во-вторых, эта система (с 2-5 веб-приложениями) была установлена ​​на десятках клиентов. Я был обеспокоен тем, что какой-нибудь админ-тупица запустит regsql, чтобы восстановить хранимые провайдером процедуры и таблицы и потерять мою работу с сохраненными процессами. - person Thomas; 05.03.2010

Возможно, сохраните значение в профиле. Настройте запись профиля в файле конфигурации и используйте ее для хранения значения.

Более реалистично, вы можете захотеть сохранить это вне таблиц ASP.NET для простоты использования и облегчения доступа к значению (возможно, за пределами веб-среды, если вам нужно) ...

Не уверен, каковы все ваши требования.

person Brian Mains    schedule 25.02.2010