L> и тут возникает вопрос, у меня у пользователя есть набор прав. когда и как его инициализивать да бы сохранить нормальную структуру расслоения?
L>то есть как вариант я вижу такой подход:
L>
L>Но мне почему то кажется каким-то кривовоатым.....
Слишком низкоуровневый интерфейс. Тот факт, что права хранятся в отдельной таблице не должен волновать пользователя. Все эти детали должны быть где-то внутри, для получения полностью инициализированного пользователя должно быть достаточно одного вызова.
Там же, возможно, придётся сделать кеширование, чтобы не создавать несколько объектов для одного пользователя (или наоборот — если нужно наоборот )
Здравствуйте, SergH, Вы писали:
SH>Здравствуйте, lumf, Вы писали:
L>>Првет все. Не могу понять как правильно должно выглядеть расслоение архитектуры. Под скажите пожалуйста.
SH>.....
L>>Таким образом если мен нужен объект по какому-нибудь пользователю. То я пишу так:
L>>
L>> и тут возникает вопрос, у меня у пользователя есть набор прав. когда и как его инициализивать да бы сохранить нормальную структуру расслоения?
L>>то есть как вариант я вижу такой подход:
L>>
L>>Но мне почему то кажется каким-то кривовоатым.....
SH>Слишком низкоуровневый интерфейс. Тот факт, что права хранятся в отдельной таблице не должен волновать пользователя. Все эти детали должны быть где-то внутри, для получения полностью инициализированного пользователя должно быть достаточно одного вызова.
SH>Там же, возможно, придётся сделать кеширование, чтобы не создавать несколько объектов для одного пользователя (или наоборот — если нужно наоборот )
да я как раз понимаю что пользователь должен инициализироваться сразу. просто меня мущает один момент.
к примеру у пользователя есть проперть
public IList<UserRight> UserRights
{
get
{
//здесь я к примеру проверяю проинициализировано ли у меня _user_rights
// и если нет то делю так?
_user_rights = UserRightsGateway.GetUserRightsById(_user_id);
return _user_rights;
}
}
Верен ли такой подход? мне почему то кажется он тоже каким-то странным.....
Здравствуйте, lumf, Вы писали:
L>Здравствуйте, SergH, Вы писали:
SH>>Здравствуйте, lumf, Вы писали:
L>
L> public IList<UserRight> UserRights
L> {
L> get
L> {
L> //здесь я к примеру проверяю проинициализировано ли у меня _user_rights
L> // и если нет то делю так?
L> _user_rights = UserRightsGateway.GetUserRightsById(_user_id);
L> return _user_rights;
L> }
L> }
L>
L>Верен ли такой подход? мне почему то кажется он тоже каким-то странным.....
Думаю, что в данном случае, пользователю не нужно знать какие у него есть права. Он ими всё равно не управляет. Права пользователя больше интересует систему внутренней безопасности приложения, чем самого пользователя.
Далее ты привязываешь права пользователя к конкретному контексту (gateway). И при этом заставлешь пользователя что-то знать о UserRightsGateway. В реале (в случае сложной CRM — отдел кадров, отдел продаж, отдел поставок) у тебя могут быть несколько подсистем куда пользователь может заходить и что-то делать (база пользователей одна, и несколько gateway в зависимости от приложения)
По секьюрити, глянь например PermissionSet. Ты же там не найдёшь свойство User. Всё определяется по контексту. Например, HttpContext.Current.User.Identity.Name
Например, тебе никто не мешает определить свои классы, имплиментирущии IPrincipal, IIdentity.
Например:
CmsPrincipal.cs
using System;
using System.Collections;
using System.Data;
using System.Security.Principal;
using System.Web;
using ClassLib.Cms.Security;
using Rsdn.Framework.Data;
namespace Web.Manager.Security
{
/// <summary>
/// Summary description for CmsPrincipal.
/// </summary>
public class CmsPrincipal : IPrincipal
{
private IIdentity _identity;
private string[] _roles;
private User _user;
private string _srid;
public User Account {
get { return _user; }
}
public string[] Roles {
get { return _roles; }
}
public string ResourceID {
get { return _srid; }
}
public CmsPrincipal(IIdentity identity, User user, string resource, string securityContext)
{
//
// TODO: Add constructor logic here
//
this._identity = identity;
this._user = user;
ArrayList roles = ClassLib.Cms.SecurityManager.LoadUserRoles(_user.ID, resource, out this._srid, securityContext);
this._roles = (string[]) roles.ToArray(typeof(string));
Array.Sort(this._roles);
}
#region IPrincipal Members
public IIdentity Identity
{
get
{
// TODO: Add CmsPrincipal.Identity getter implementation
return _identity;
}
}
public bool IsInRole(string role)
{
// TODO: Add CmsPrincipal.IsInRole implementation
return Array.BinarySearch(_roles, role) >= 0;
}
#endregion
/// <summary>
/// Checks whether a principal is in all of the specified set of roles
/// </summary>
/// <param name="roles">Array of strings. Set of roles to check</param>
/// <returns></returns>
public bool IsInAllRoles( params string [] roles )
{
foreach (string searchrole in roles )
{
if (Array.BinarySearch(_roles, searchrole) < 0 )
return false;
}
return true;
}
/// <summary>
/// Checks whether a principal is in any of the specified set of roles
/// </summary>
/// <param name="roles">Array of strings. Set of roles to check</param>
/// <returns></returns>
public bool IsInAnyRoles( params string [] roles )
{
foreach (string searchrole in roles )
{
if (Array.BinarySearch(_roles, searchrole ) >= 0 )
return true;
}
return false;
}
}
}
-----------------------------------------------------
global.asax.cs
.......
........
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
//Response.Write(String.Format("Application_AuthenticateRequest: {0} | {1}<br>", Request.Path, Request.ApplicationPath));
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = Context.Request.Cookies[cookieName];
if(null == authCookie) {
// There is no authentication cookie.
return;
}
FormsAuthenticationTicket authTicket = null;
try {
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
} catch(Exception ex) {
// Log exception details (omitted for simplicity)
//Response.Write("decryption error:"+e.ToString()+".<br>");
return;
}
if (null == authTicket) {
// Cookie failed to decrypt.
//Response.Write("failed to decrypt.<br>");
return;
}
// When the ticket was created, the UserData property was assigned a
// unique identificator of related database entry.
User user = UserManager.Load(authTicket.UserData);
// Create an Identity object
FormsIdentity id = new FormsIdentity( authTicket );
string contextPath = Context.Request.Path.Replace(Context.Request.ApplicationPath, "");
// This principal will flow throughout the request.
CmsPrincipal principal = new CmsPrincipal(id, user, contextPath, System.Configuration.ConfigurationSettings.AppSettings["SecurityContext"] != null?System.Configuration.ConfigurationSettings.AppSettings["SecurityContext"].ToString():"");
// Attach the new principal object to the current HttpContext object
Context.User = principal;
}
.......
.......
Т.е. в данном случае, мы имеем данные о пользователе в свойстве
((CmsPrincipal) HttpContext.Current.User).Account,
а его права в ((CmsPrincipal) HttpContext.Current.User).Roles.
Здравствуйте, Taison, Вы писали:
T>Здравствуйте, lumf, Вы писали:
L>>Здравствуйте, SergH, Вы писали:
SH>>>Здравствуйте, lumf, Вы писали:
L>>
L>> public IList<UserRight> UserRights
L>> {
L>> get
L>> {
L>> //здесь я к примеру проверяю проинициализировано ли у меня _user_rights
L>> // и если нет то делю так?
L>> _user_rights = UserRightsGateway.GetUserRightsById(_user_id);
L>> return _user_rights;
L>> }
L>> }
L>>
L>>Верен ли такой подход? мне почему то кажется он тоже каким-то странным.....
T>Думаю, что в данном случае, пользователю не нужно знать какие у него есть права. Он ими всё равно не управляет. Права пользователя больше интересует систему внутренней безопасности приложения, чем самого пользователя.
Что-то запутался Ну почему пользоватлею все равно... это же его права? его. у пользователя по идее должны быть методы добавления.удаления прав. А так же метод в который я передаю что-то, чтобы проверить имеет ли данный пользователь доступ к этому.
T>Далее ты привязываешь права пользователя к конкретному контексту (gateway). И при этом заставлешь пользователя что-то знать о UserRightsGateway. В реале (в случае сложной CRM — отдел кадров, отдел продаж, отдел поставок) у тебя могут быть несколько подсистем куда пользователь может заходить и что-то делать (база пользователей одна, и несколько gateway в зависимости от приложения)
можно вот тут чуть подробнее.... а то я догнать не могу.....
L>Верен ли такой подход? мне почему то кажется он тоже каким-то странным.....
Именно такой -- неверен. Причин несколько:
Бизнес-логика становится зависимой от DAL'а (то есть вообще кольцевые зависимости)
Если UserRightsGateway — синглтон, то сильно ухудшается тестируемость (testability)
Если UserRightsGateway — свойство User типа IUserRightsGateway, то встает вопрос о получении корректной реализации этого интерфейса
Вообще, самый корректный вариант -- это Domain Model, состоящая из простых и понятных объектов, безо всякой прикладной логики. То есть бизнес-объекты выступают просто в виде контейнеров данных. Вариантов работы с такими объектами тоже много. Например, использовать какое-нибудь ORM-средство, которое возьмет на себя всю работу по загрузке, сохранению и обновлению объектов. В этом случае получается богатая объектная модель -- с ассоциациями, коллекциями, наследованием и прочими вещами. Но местные гуру такой подход почему-то не очень одобряют. Как бы то ни было, в этом случае код работы с пользователями может выглядеть как-то так (пользователь пытается отредактировать что-то и у этого "что-то" есть история изменений):
User user = UserGateway.GetUserByLogin(login);
if(user.HasRight(UserRight.Edit)) // UserRight - перечисление
{
ContentRevision revision = new ContentRevision();
revision.EditedBy = user;
Content content = ContentGateway.GetContentByTimestamp(timestamp);
content.AddRevision(revision);
content.LastModifiedOn = DateTime.Now;
ContentGateway.SaveContent(content);
} // if
Второй вариант — это (как я его для себя определяю) использование какого-нибудь Result-Set Mapper'а. В этом случае получаем более бедную объектную модель, представляющую собой классы, один к одному соответствующие таблицам в БД. Тот же фрагмент кода может выглядеть примерно так:
User user = UserGateway.GetUserByLogin(login);
if(UserRightService.HasRight(user, UserRight.Edit))
{
Content content = ContentGateway.GetContentByTimestamp(timestamp);
ContentRevision revision = new ContentRevision();
revision.EditedBy = user.ID;
revision.Content = content.ID;
ContentRevisionGateway.SaveContentRevision(revision);
content.LastModifiedOn = DateTime.Now;
ContentGateway.SaveContent(content);
} // if
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
ну вот я практически так и пытаюсь сделать только вот не могу понять как правильно загрузить права пользователя в user. Информация по пользователю гружу через BLToolkit именно вот в этом методе UserGateway.GetUserByLogin(login); но как вот сделать так чтобы и права грузились я не могу понять.... а самое главное верно ли будет подгружать права пользователя именно вэтом контексте? типа я при инициаизации забрал и информацию о пользователе, а заодно и права пользователя, а заодно может там и еще какие-то свойства пользователся, которые лежат в другой таблице (к примеру ip адреса с которых ему можно логиниться в систему)......
Здравствуйте, lumf, Вы писали:
L>ну вот я практически так и пытаюсь сделать
Нет. У вас, если используете BLT (и если я его правильно помню), должен быть второй вариант.
L>только вот не могу понять как правильно загрузить права пользователя в user. Информация по пользователю гружу через BLToolkit именно вот в этом методе UserGateway.GetUserByLogin(login); но как вот сделать так чтобы и права грузились я не могу понять.... а самое главное верно ли будет подгружать права пользователя именно вэтом контексте? типа я при инициаизации забрал и информацию о пользователе, а заодно и права пользователя, а заодно может там и еще какие-то свойства пользователся, которые лежат в другой таблице (к примеру ip адреса с которых ему можно логиниться в систему)......
А нужна ли вся эта информация? То есть, всегда ли надо загружать информацию о правах, свойствах и т.д.?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
Н>А нужна ли вся эта информация? То есть, всегда ли надо загружать информацию о правах, свойствах и т.д.?
во, точно анужнали она вообще.... давай загрузим ее потом как она понадобится (отложим)...... и как мне заполнить какое либо из полей в этом случае? кроме как изнутри?
Здравствуйте, lumf, Вы писали:
L>Дальше что я должен вызвать чтобы сохранить эти права?
Я не знаю, как это делается в BLT, но общие соображение следующие.
Класс UserRight имеет два свойства — UserID и RightID. Иными словами, он представляет собой таблицу-связку UserRightJunction, используемую для моделирования отношения "многие-ко-многим" в SQL. Таким образом, добавление роли пользователю сводится к добавлению записи в таблицу, что может выглядель примерно так:
User user = UserGateway.GetUserByLogin(login);
UserRightGateway.SaveUserRight(new UserRight(user.ID, UserRight.Edit));
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
Здравствуйте, Нахлобуч, Вы писали:
Н>Здравствуйте, lumf, Вы писали:
L>>и как мне заполнить какое либо из полей в этом случае? кроме как изнутри?
Н>Не понял вот этого.
к примеру у пользователя есть проперть
public IList<UserRight> UserRights
{
get
{
//здесь я к примеру проверяю проинициализировано ли у меня _user_rights
// и если нет то делю так?
_user_rights = UserRightsGateway.GetUserRightsById(_user_id);
return _user_rights;
}
}
Здравствуйте, lumf, Вы писали:
L>это разве не lazy load ?
В целом -- да. Но я уже описал недостатки такой реализации. Lazy Load тоже зверь не без проблем, но я не к нему веду. Я говорю о том, что в случае рукопашной реализации и простой Domain Model надо загружать только те данные, которые необходимы в данный конкретный момент.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
T>>Думаю, что в данном случае, пользователю не нужно знать какие у него есть права. Он ими всё равно не управляет. Права пользователя больше интересует систему внутренней безопасности приложения, чем самого пользователя.
L>Что-то запутался Ну почему пользоватлею все равно... это же его права? его. у пользователя по идее должны быть методы добавления.удаления прав. А так же метод в который я передаю что-то, чтобы проверить имеет ли данный пользователь доступ к этому.
T>>Далее ты привязываешь права пользователя к конкретному контексту (gateway). И при этом заставлешь пользователя что-то знать о UserRightsGateway. В реале (в случае сложной CRM — отдел кадров, отдел продаж, отдел поставок) у тебя могут быть несколько подсистем куда пользователь может заходить и что-то делать (база пользователей одна, и несколько gateway в зависимости от приложения)
L>можно вот тут чуть подробнее.... а то я догнать не могу.....
Ну например. Представь СТУДЕНТА как пользователя (IPrincipal). И вот он каждый день посещает институт, где существует система пропусков, а также ходит на работу, где установлена своя система пропусков. Как пользователь (человек ) он один, а систем пропусков — две. В каждой из них у него свои права(роли). Всё зависит от контекста. И каждый раз при входе требуется пройти авторизацию. В примере, в обеих случаях её выполняет пропуск (IIdentity).
Далее, как работник (EmployeePrincipal, студент — StudentPrincipal), пользователь по пропуску может посещать только определённые помещения, отделы. При этом только система безопасности предприятия определяет доступ пользователя, а не система безопасности института.
Т.е. получаем, что пользователь не должен знать ничего о своих правах, как равно возможность их добавлять, удалять. В идеале, пользователь может попытаться сделать всё, — весь вопрос в том, позволит ли система ему это сделать.
Проверка прав пользователя должна осуществляться в системе другим способом:
Как я упоминал, глянь класс PermissionSet, а также IPermission, PrincipalPermission в MSDN
Например, на предприятии существуют следующие группы пользователей (роли): рабочие, бригадиры, директора.
И есть операция 'Назначить задание'. Задания могут быть трёх типов: глобальный, общие, отдельные задания.
Глобальные могут назначать только директора.
Общие — и диретора, и бригадиры.
Отдельные задания — только бригадиры
Получаем:
public class Enterprise
{
protected static PrincipalPermission HighestAccessPermission = new PrincipalPermission(null, "Director");
protected static PrincipalPermission HighAccessPermission = new PrincipalPermission(null, "Master");
protected static PrincipalPermission LowAccessPermission = new PrincipalPermission(null, "Employee");
.................
.................
public void AssignGlobalTask(ITaskInfo task, User responsible) {
HighestAccessPermission.Demand(); // Только директор может назначить глобальную задачу
// Do assign global task
}
public void AssignCommonTask(ITaskInfo task, User responsibles[]) {
LowAccessPermission.Deny(); // Запретить рабочим назначать общую задачу, только директора и бригадиры
// Do assign common task
}
public void AssignSeparateTask(ITaskInfo task, User responsibles[]) {
HighAccessPermission.Demand();
// Do assign separate task
}
// Ещё можно декларативно
[PrincipalPermission(SecurityAction.Demand, Role="Director")] // Только директор может назначить глобальную задачуpublic void AssignGlobalTask(ITaskInfo task, User responsible) {
// Do assign global task
}
[PrincipalPermission(SecurityAction.Deny, Role="Employee")] // Запретить рабочим назначать общую задачу, только директора и бригадирыpublic void AssignCommonTask(ITaskInfo task, User responsibles[]) {
// Do assign common task
}
[PrincipalPermission(SecurityAction.Demand, Role="Master")]
public void AssignSeparateTask(ITaskInfo task, User responsibles[]) {
// Do assign separate task
}
.................
}
Здравствуйте, Нахлобуч, Вы писали:
Н>Здравствуйте, lumf, Вы писали:
L>>Дальше что я должен вызвать чтобы сохранить эти права?
Н>Я не знаю, как это делается в BLT, но общие соображение следующие. Н>Класс UserRight имеет два свойства — UserID и RightID. Иными словами, он представляет собой таблицу-связку UserRightJunction, используемую для моделирования отношения "многие-ко-многим" в SQL. Таким образом, добавление роли пользователю сводится к добавлению записи в таблицу, что может выглядель примерно так:
Н>
Н>User user = UserGateway.GetUserByLogin(login);
Н>UserRightGateway.SaveUserRight(new UserRight(user.ID, UserRight.Edit));
Н>
таким образом все опять сводится к тому что на каждый класс у меня есть своя таблица....
и все эти классы являются RowGateway (по фаулеру)
просто я немного не допонимаю.... если все эти классы разрознены и связаны лишь id
то как им взаимодействовать?
то есть если эти классы не включаются друг в друга...
к примеру веб-приложение:
я аутентифицировал пользователя, создал объект и положил его в сессию:
User user = UserGateway.GetUserByLogin(login);
так же есть набор прав данного пользователя и по идее тоже кладу в сессию (где-то же надо хранить, ну а раз не вобекте пользователя то в сессии)
вот пришел я на опрделенную страницу... и хочу узнать а есть ли у пользователя права на что нибудь?
у какого класса должен быть метод для этой проверки?
помогите осознать плиз...хочется освоить различную архитектуру... но на деле объектную модель не получается пострить потому что не понятно как это вообще делается нормально
Здравствуйте, lumf, Вы писали:
L>вот пришел я на опрделенную страницу... и хочу узнать а есть ли у пользователя права на что нибудь?
Вот на той определенной странице и запросите права Дерните UserRightGateway.GetUserRight() и проверяйте.
L>у какого класса должен быть метод для этой проверки?
Это вам решать. Можно непосредственно в коде страницы. На первое время этого будет достаточно, а потом покумекаете и выделите в отдельные сервисные объекты.
Не стоит сразу пытаться постичь все. Не выйдет, да и голова сразу столько информации не переварит. Двигайтесь последовательно.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows