415 lines · 19.6 KB · Text
RawDownload
@*******************************************************************************************************//  Groups.cshtml - Gbtc////  Copyright © 2016, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this//  file except in compliance with the License. You may obtain a copy of the License at:////      http://opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  03/03/2016 - J. Ritchie Carroll//       Generated original version of source code.//////******************************************************************************************************//  To use in an ASP.NET destination project, include a Groups.cshtml view with following content:////  @using GSF.Web//  @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection?.ToString())}//  @{ViewBag.HelpTarget = Url.Action("Help", "Main");}//  @Html.RenderResource("GSF.Web.Security.Views.Groups.cshtml")//  @section Scripts{@Html.Raw(ViewBag.ScriptsSection?.ToString())}////******************************************************************************************************//  To use in a self-hosted web project, include a Groups.cshtml view with following content:////  @using GSF.Web.Model//  @inherits ExtendedTemplateBase<AppModel>//  @section StyleSheets{@Html.Raw(ViewBag.StyleSheetsSection.ToString())}//  @{Layout = "Layout.cshtml";}//  @Html.RenderResource("GSF.Web.Security.Views.Groups.cshtml")//  @section Scripts{@Html.Raw(ViewBag.ScriptsSection.ToString())}////*****************************************************************************************************@@using GSF.Configuration@using GSF.Data.Model@using GSF.Security@using GSF.Security.Model@using GSF.Web.Model@using GSF.Web.Security@inherits ExtendedTemplateBase@section StyleSheets{    <style>         .account-feedback-wrapper {             position: relative;             display: table;         }          .account-feedback-message {             position: absolute;             vertical-align: middle;             display: table-cell;             left: 10px;             width: 425px;             display: none;         }          .account-feedback-icon {             font-size: large;             vertical-align: middle;             margin-top: -5px;         }    </style>}@{    // Layout will depend on rendering environment, so do not specify one    DataContext dataContext = ViewBag.DataContext;    ViewBag.Title = "Groups";     // Define column headers, use null for field name to make column non-sortable    ViewBag.HeaderColumns = new[]    {   //    { "Field", "Label", "Classes" }        new[] { null, "Group Name", "text-center valign-middle" },        new[] { null, "Is&nbsp;AD&nbsp;Group", "text-center valign-middle" },        new[] { "CreatedOn", "Date&nbsp;Added", "text-center" },        new[] { null, "Group Roles", "text-center valign-middle" }    };     if (ViewBag.HelpTarget == null) {        ViewBag.HelpTarget = "Help.cshtml";    }     // Make sure default settings exist    ConfigurationFile config = ConfigurationFile.Current;    CategorizedSettingsElementCollection settings = config.Settings[SecurityProviderBase.DefaultSettingsCategory];     settings.Add("ApplicationName", SecurityProviderBase.DefaultApplicationName, "Name of the application being secured as defined in the backend security data store.");     // Get configured settings    string applicationName = settings["ApplicationName"].ValueAs(SecurityProviderBase.DefaultApplicationName);     ViewBag.BodyRows = BodyRows().ToString();    ViewBag.AddNewEditDialog = AddNewEditDialog(dataContext).ToString();    ViewBag.AddNewEditTitle = "Group";}@helper BodyRows(){    <td width="65%" class="text-center valign-middle" nowrap><button type="button" class="btn btn-link" data-bind="text: AccountName(), attr: {title: Name}, click: $parent.viewPageRecord"></button></td>    <td width="10%" class="text-center valign-middle"><input type="checkbox" id="checkEnabled" data-bind="checked: useADAuthentication(AccountName())" disabled></td>    <td width="10%" class="text-center valign-middle" data-bind="text: CreatedOn.formatDate(DateFormat)"></td>    <td width="10%" class="text-center valign-middle"><button type="button" class="btn btn-default btn-sm" data-bind="click: openRolesEditor.bind($data), enable: $parent.canEdit() && $parent.dataHubIsConnected()">Roles...</button></td>    <td width="5%" class="text-center valign-middle" nowrap>        <button type="button" class="btn btn-xs" data-bind="click: $parent.editPageRecord, enable: $parent.canEdit() && $parent.dataHubIsConnected()"><span class="glyphicon glyphicon-pencil"></span></button>        <button type="button" class="btn btn-xs" data-bind="click: $parent.removePageRecord, enable: $parent.canDelete() && $parent.dataHubIsConnected()"><span class="glyphicon glyphicon-remove"></span></button>    </td>}@helper AccountValidationFeedback(){    <div class="pull-right account-feedback-wrapper">        <span id="resolvingAccount" class="account-feedback-message"><span class="glyphicon glyphicon-refresh glyphicon-spin account-feedback-icon"></span>&nbsp;<em class="small">Resolving account details...</em></span>        <span id="accountValid" class="account-feedback-message"><span class="glyphicon glyphicon-ok-circle account-feedback-icon" data-bind="style: {color: $root.recordMode()===RecordMode.View ? 'gray' : 'green'}"></span>&nbsp;<em class="small">Resolved account name</em></span>        <span id="accountInvalid" class="account-feedback-message"><span class="glyphicon glyphicon-remove-circle account-feedback-icon" data-bind="style: {color: $root.recordMode()===RecordMode.View ? 'gray' : 'red'}"></span>&nbsp;<em class="small">Cannot resolve account name</em></span>        <span id="accountUnknown" class="account-feedback-message"><span class="glyphicon glyphicon-warning-sign account-feedback-icon" data-bind="style: {color: $root.recordMode()===RecordMode.View ? 'gray' : 'orange'}"></span>&nbsp;<em class="small">Valid account name is not a group</em></span>    </div>}@helper AddNewEditDialog(DataContext dataContext){    <div class="col-md-12">        @Raw(dataContext.AddInputField<SecurityGroup>("Name", fieldLabel: "Name" + AccountValidationFeedback().ToString().Trim(), customDataBinding: "attr: {'acct-valid': validateAccount($data), 'autocomplete': 'off'}", initialFocus: true))        <div class="panel panel-default" data-bind="css: {'panel-readonly': $root.recordMode()===RecordMode.View, 'panel-primary': UseDBAuthentication}">            <div class="panel-heading">                <div class="row">                    <div class="col-md-8">                        <div class="row">                            <div class="col-md-4">                                <label class="radio-inline"><input type="radio" name="radioUseDBAuthentication" data-bind="checkedValue: false, checked: UseDBAuthentication, attr: {'disabled': ($root.recordMode()===RecordMode.View ? true : undefined)}">&nbsp;Active&nbsp;Directory&nbsp;Group</label>                            </div>                            <div class="col-md-8">                                <label class="radio-inline"><input type="radio" name="radioUseDBAuthentication" data-bind="checkedValue: true, checked: UseDBAuthentication, attr: {'disabled': ($root.recordMode()===RecordMode.View ? true : undefined)}">&nbsp;Database&nbsp;Group</label>                            </div>                        </div>                    </div>                    <div class="col-md-4">                    </div>                </div>            </div>            <div class="panel-body" data-bind="visible: UseDBAuthentication">                @Raw(dataContext.AddTextAreaField<SecurityGroup>("Description", 4, dependencyFieldName: "UseDBAuthentication"))            </div>        </div>    </div>}@Html.RenderResource("GSF.Web.Model.Views.PagedViewModel.cshtml")<div id="editRolesDialog" class="modal fade" role="dialog">    <div class="modal-dialog">        <div class="modal-content">            <div class="modal-header">                <button type="button" class="close" data-dismiss="modal">&times;</button>                <h4 class="modal-title">Edit Roles for <span id="currentGroupName"></span>:</h4>            </div>            <div class="modal-body">                <div class="modal-body auto-height text-center">                    <form role="form">                        <input type="hidden" id="currentGroupID" />                        <div class="center-block">                            <div id="loadingRoles">Loading role assignments&nbsp;&nbsp;<span class="glyphicon glyphicon-refresh glyphicon-spin"></span><hr class="full-break" /></div>                            <table class="table-condensed table-center">                            @foreach (ApplicationRole role in dataContext.Table<ApplicationRole>().QueryRecords("Name", new RecordRestriction("NodeID={0}", SecurityHub.DefaultNodeID))) {                                <tr>                                    <td class="text-left valign-middle">                                        <div class="checkbox" style="margin: 0">                                            <label for="@role.ID"><input type="checkbox" id="@role.ID"> @role.Description (@role.Name)</label>                                        </div>                                    </td>                                </tr>                            }                            </table>                            <br />                            <div class="text-center">                                <a href="@ViewBag.HelpTarget#Roles" target="_blank">@applicationName Role Definitions</a>                            </div>                        </div>                    </form>                </div>            </div>            <div class="modal-footer">                <button type="submit" class="btn btn-primary" data-dismiss="modal" id="saveRolesButton" data-bind="enable: canEdit()">Save</button>                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>            </div>        </div>    </div></div>@section Scripts {    <script>        "use strict";         @Raw(dataContext.RenderViewModelConfiguration<SecurityGroup, SecurityHub>(ViewBag))        var AccountState = {            Invalid: 0,            Valid: 1,            Unknown: 2,            Resolving: 3        }         function lookupAccountName(record) {            if (viewModel.dataHubIsConnected()) {                securityHub.sidToAccountName(record.Name).done(function (accountName) {                    record.AccountName(accountName);                });            }        }         function validateAccount(observableRecord) {            showAccountNameValidityFeedback(AccountState.Resolving);             if (observableRecord.UseDBAuthentication == null || observableRecord.UseDBAuthentication()) {                hideAccountNameValidityFeedback();            }            else {                if (viewModel.dataHubIsConnected()) {                    const accountName = observableRecord.Name();                     if (isEmpty(accountName)) {                        showAccountNameValidityFeedback(AccountState.Invalid);                    } else {                        securityHub.groupNameToSID(accountName).done(function (sid) {                            if (accountName !== sid && accountName.countOccurrences("\\") < 2) {                                if (accountName.startsWith("NT AUTHORITY")) {                                    // This is a valid group source for things like "Authenticated Users", but is                                    // not technically a "group" - since SID validated, we report a valid account                                    showAccountNameValidityFeedback(AccountState.Valid);                                } else {                                    securityHub.isGroupSID(sid).done(function (isGroup) {                                        showAccountNameValidityFeedback(isGroup ? AccountState.Valid : AccountState.Unknown);                                    });                                }                            } else {                                showAccountNameValidityFeedback(AccountState.Invalid);                            }                        });                    }                }                else {                    hideAccountNameValidityFeedback();                }            }             return undefined;        }         var messages = [];        var nextMessageName = undefined;        var showingMessage = false;         messages.push("resolvingAccount");        messages.push("accountValid");        messages.push("accountInvalid");        messages.push("accountUnknown");         function showFeedbackMessage(messageName) {            if (showingMessage) {                nextMessageName = messageName;            } else {                showingMessage = true;                 hideAccountNameValidityFeedback(messageName);                 if (messageName == null)                    showingMessage = false;                else                    $("#" + messageName).fadeIn(function () {                        showingMessage = false;                         if (nextMessageName != undefined) {                            messageName = nextMessageName;                            nextMessageName = undefined;                            showFeedbackMessage(messageName);                        }                    });            }        }         function hideAccountNameValidityFeedback(messageName) {            for (let i = 0; i < messages.length; i++) {                if (messages[i] !== messageName)                    $("#" + messages[i]).hide();            }        }         function showAccountNameValidityFeedback(accountState) {            switch (accountState) {                case AccountState.Valid:                    showFeedbackMessage("accountValid");                    break;                case AccountState.Invalid:                    showFeedbackMessage("accountInvalid");                    break;                case AccountState.Unknown:                    showFeedbackMessage("accountUnknown");                    break;                case AccountState.Resolving:                    showFeedbackMessage("resolvingAccount");                    break;            }        }         function useADAuthentication(accountName)        {            const groupName = notNull(accountName);             // Just deriving if group is AD based by delimiters in group name            if (groupName.length > 0)                return (groupName.indexOf("\\") > 0 || groupName.indexOf("@@") > 0 || groupName.startsWith("S-"));             return true; // Default to AD group        }         $(viewModel).on("pageRecordsQueried", function (event, records) {            // Add a virtual observable field called "AccountName" that will resolve SID values to account names            for (var i = 0; i < records.length; i++) {                records[i].AccountName = ko.observable("...");                lookupAccountName(records[i]);            }        });         $(viewModel).on("newRecord", function (event, newRecord) {            // Make sure new records get virtual fields            newRecord.AccountName = ko.observable("");        });         $(viewModel).on("beforeEdit", function (event, observableRecord) {            // Load human readable account name into Name field            if (observableRecord.AccountName)                observableRecord.Name(observableRecord.AccountName());                            // Create a virtual UseDBAuthentication field            observableRecord.UseDBAuthentication = ko.observable(!useADAuthentication(notNull(observableRecord.Name())));        });         $(viewModel).on("beforeSave", function (event, observableRecord, promises) {            const isADGroup = !observableRecord.UseDBAuthentication();             // Remove virtual observable fields before serialization for record updates            delete observableRecord.UseDBAuthentication;            delete observableRecord.AccountName;             // Push promise to convert group name back to SID before updating record            if (viewModel.dataHubIsConnected() && isADGroup) {                promises.push($.Deferred(function (deferred) {                    securityHub.groupNameToSID(observableRecord.Name()).done(function (sid) {                        observableRecord.Name(sid);                        deferred.resolve();                    });                }).promise());            }        });         function openRolesEditor(record) {            $("#currentGroupID").text(record.ID);            $("#currentGroupName").text(record.AccountName());            $("#editRolesDialog input:checkbox").prop("checked", false);             if (viewModel.dataHubIsConnected()) {                securityHub.queryApplicationRoles().done(function (roles) {                    var evaluations = roles.length;                     if (evaluations > 0)                        $("#loadingRoles").show();                     for (var i = 0; i < roles.length; i++) {                        securityHub.groupIsInRole(record.ID, roles[i].ID).done(function (roleID) {                            return function (result) {                                $("#" + roleID).prop("checked", result);                                 evaluations--;                                 if (evaluations <= 0)                                    $("#loadingRoles").hide();                            };                        }(roles[i].ID));                    }                    $("#editRolesDialog").modal("show");                });            }        }         $(function () {            $("#editRolesDialog").modal({ show: false, backdrop: "static" });             $("#saveRolesButton").click(function () {                if (viewModel.dataHubIsConnected()) {                    const groupID = $("#currentGroupID").text();                    const roles = $("#editRolesDialog input:checkbox");                     for (var i = 0; i < roles.length; i++) {                        const element = $(roles[i]);                        const roleID = element.attr("id");                         if (element.prop("checked")) {                            securityHub.addGroupToRole(groupID, roleID).fail(function (error) {                                showErrorMessage(error, null, true);                            });                        } else {                            securityHub.removeGroupFromRole(groupID, roleID).fail(function (error) {                                showErrorMessage(error, null, true);                            });                        }                    }                }            });        });    </script>}@{    ViewBag.StyleSheetsSection = RenderSection("StyleSheets").ToString();    ViewBag.ScriptsSection = RenderSection("Scripts").ToString();}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t