Управление членом группы Active Directory через сайт ASP.NET в MVC


Фон

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

Сайт был второстепенным для более крупного проекта, и хотя обычно мы строим угловой курорты с веб ASP.NET API и бэкэнд-было решено, что это должен быть достаточно простой сайт ASP.NET в MVC.

Руководство пользователя

Управляющий групп Active Directory, используя обычные средства Windows, является довольно громоздким excperience, и, таким образом, мы, как правило, реализуют только для администратора страницы управления в наших проектах, где мы можем легко искать пользователей и добавлять их или удалять их из различных активных групп каталога. Мы обычно копипаст этих реализаций и изменить необходимые переменные (названия групп и т. д.), но это не может быть сделано сейчас с переднего плана в угловой (код веб ASP.NET API и бэкэнд-во многом могла быть использована повторно).

И поэтому я быстренько на скорую руку простую реализацию в ASP.NET в MVC. Правда, пока он работает, я не счастлив с одной части реализации. Я подозреваю, что я не очень хорошо разбирается достаточно в ASP.NET MVC и поэтому я надеюсь, что кто-то может придумать лучшее решение.

Пользователь

public class User
{
    public User(string userName, string displayName)
    {
        UserName = userName;
        DisplayName = displayName;
    }

    public string UserName { get; set; }

    public string DisplayName { get; set; }
}

Роль

public class Role
{
    public Role(string name, string shortName)
    {
        Name = name;
        ShortName = shortName;
        Users = new List<User>();
    }

    public string Name { get; set; }

    public string ShortName { get; set; }

    public List<User> Users { get; set; }

    public static string GetShortName(string name, string domainAndPrefix)
    {
        return name.Replace(domainAndPrefix, string.Empty);
    }

    public static string GetProperName(string name, string domainAndPrefix)
    {
        if (name.StartsWith(domainAndPrefix))
        {
            return name;
        }

        return domainAndPrefix + name;
    }
}

Из ActiveDirectory

public static class ActiveDirectory
{
    public const string Domain = "MyDomain";
    public const string Prefix = "MyProject-";
    public const string DomainAndPrefix = Domain + @"\" + Prefix;

    public static class Role
    {
        public const string Administrators = DomainAndPrefix + "Administrators";
        public const string Readers = DomainAndPrefix + "Readers";
        public const string Editors = DomainAndPrefix + "Editors";

        public static string[] All = { Administrators, Readers, Editors };
    }

    public static class RoleGroup
    {
        public const string Admins = Role.Administrators;
        public const string ReadersAndAdmins = Role.Readers + "," + Role.Administrators;
        public const string EditorsAndAdmins = Role.Editors + "," + Role.Administrators;
    }
}

ISecurityFacade

public interface ISecurityFacade
{
    UserDataResponse GetAll(UserDataRequest request);
}

SecurityFacade

public class SecurityFacade : ISecurityFacade
{
    public UserDataResponse GetAll(UserDataRequest request)
    {
        var roles = ActiveDirectory.Role.All
            .Select(GetRole)
            .OrderBy(x => x.ShortName)
            .ToList();

        if (string.IsNullOrEmpty(request.RoleName))
        {
            request.RoleName = roles.First().ShortName;
        }

        var response = new UserDataResponse(request)
        {
            Roles = roles
        };

        return response;
    }

    private Role GetRole(string group)
    {
        return new Role(group, Role.GetShortName(group, ActiveDirectory.DomainAndPrefix))
        {
            Users = UsersRetriever.GetUsersInRole(group)
        };
    }
}

AdminController

public class AdminController : AdfsController
{
    private readonly ISecurityFacade _securityFacade;

    public AdminController()
    {
        _securityFacade = new SecurityFacade();
    }

    [Authorize(Roles = ActiveDirectory.RoleGroup.Admins)]
    [HttpGet]
    public ActionResult Index(string roleName)
    {
        ViewBag.Title = "Users & roles";

        if (string.IsNullOrEmpty(roleName))
        {
            // hacky solution that is necessary due to data binding
            roleName = Request.QueryString["Request.RoleName"];

            if (string.IsNullOrEmpty(roleName))
            {
                roleName = Role.GetShortName(ActiveDirectory.Role.Administrators, ActiveDirectory.DomainAndPrefix);
            }
        }

        var request = new UserDataRequest { RoleName = roleName };

        var response = _securityFacade.GetAll(request);

        return View(response);
    }
}

AdfsController

public class AdfsController : Controller
{
    protected override void OnAuthentication(AuthenticationContext filterContext)
    {
        base.OnAuthentication(filterContext);

        SetCurrentUser(filterContext.Principal);
    }

    private void SetCurrentUser(IPrincipal principal)
    {
        var claimsIdentity = (ClaimsIdentity)((ClaimsPrincipal)principal).Identity;

        SetNameIdentifier(claimsIdentity);
        SetRoles(claimsIdentity);
    }

    private void SetNameIdentifier(ClaimsIdentity claimsIdentity)
    {
        var name = claimsIdentity.Claims.Single(x => x.Type == ClaimTypes.Name);
        var nameIdentifier = claimsIdentity.Claims.SingleOrDefault(x => x.Type == ClaimTypes.NameIdentifier);

        if (string.IsNullOrEmpty(nameIdentifier?.Value))
        {
            claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, name.Value));
        }
    }

    private void SetRoles(ClaimsIdentity claimsIdentity)
    {
        var roles = User.Identity.GetEliseForwardRoles();

        foreach (var role in roles)
        {
            claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
        }
    }
}

UserDataRequest

public class UserDataRequest
{
    public string RoleName { get; set; }

    public int Page { get; set; }

    public int ItemsPerPage { get; set; }
}

UserDataResponse

public class UserDataResponse
{
    public UserDataResponse(UserDataRequest request)
    {
        Request = request;
    }

    public UserDataRequest Request { get; }

    public List<Role> Roles { get; set; }

    public List<User> Users
    {
        get { return Roles.Single(x => x.ShortName == Request.RoleName).Users; }
    }

    public int Count { get; set; }
}

Вид Индекса / Админ /.cshtml по

@model UserDataResponse

<h2>@ViewBag.Title</h2>

<div>
    @using (Html.BeginForm("Index", "Admin", FormMethod.Get))
    {
        foreach (var role in Model.Roles)
        {
            @Html.RadioButtonFor(x => x.Request.RoleName, role.ShortName) @Html.Label(role.ShortName) <span>&nbsp;</span>
        }

        <input type="submit" name="submit" value="Filter" class="btn btn-default" />
    }
</div>

<div>
    <table class="table table-striped table-bordered table-hover">
        <thead>
            <tr>
                <th>Name</th>
                <th>Login</th>
                <th>&nbsp;</th>
            </tr>
        </thead>

        <tbody>
            @if (Model.Users.Any())
            {
                foreach (var user in Model.Users)
                {
                    <tr>
                        <td>@Html.DisplayFor(modelItem => user.DisplayName)</td>
                        <td>@Html.DisplayFor(modelItem => user.UserName)</td>
                        <td>@Html.ActionLink("Remove", "Remove", new { userName = user.UserName, displayName = user.DisplayName, roleName = Model.Request.RoleName })</td>
                    </tr>
                }
            }
            else
            {
                <tr>
                    <td colspan="3">Found no results.</td>
                </tr>
            }
        </tbody>

        <tfoot>
            <tr>
                <td colspan="3" class="pager">
                    @Html.Pager(Model.Request.ItemsPerPage, Model.Request.Page, Model.Count).Options(x =>
                             x.AddRouteValue("Request.RoleName", Model.Request.RoleName)
                                 .DisplayFirstAndLastPage()
                                 .SetFirstPageText("First")
                                 .SetLastPageText("Last")
                                 .SetPreviousPageText("Prev")
                                 .SetNextPageText("Next")
                             )
                </td>
            </tr>
        </tfoot>
    </table>
</div>

<div>
    @Html.ActionLink("Add user", "AddUser", new { roleName = Model.Request.RoleName })
</div>

Что я недоволен

Как вы можете догадаться, моя главная проблема с public ActionResult Index(string roleName): параметр roleName на самом деле не заполнено и его значение в строку запроса параметр "Request.RoleName". Конечно, при первом входе на страницу нет никакого значения и, следовательно, по умолчанию присваивается (так что вы не в конечном итоге с пустой страницы):

roleName = Role.GetShortName(ActiveDirectory.Role.Administrators, ActiveDirectory.DomainAndPrefix);

Для меня все это выглядит "не правильно":

if (string.IsNullOrEmpty(roleName))
{
    // hacky solution that is necessary due to data binding
    roleName = Request.QueryString["Request.RoleName"];

    if (string.IsNullOrEmpty(roleName))
    {
        roleName = Role.GetShortName(ActiveDirectory.Role.Administrators, ActiveDirectory.DomainAndPrefix);
    }
}

var request = new UserDataRequest { RoleName = roleName };

Где я ошибся? Есть ли более элегантный способ привязать кнопки радио в целях код в AdminController?



Комментарии