diff --git a/GanjooRazor/Areas/Admin/Pages/Roles.cshtml b/GanjooRazor/Areas/Admin/Pages/Roles.cshtml new file mode 100644 index 00000000..a2c9e34b --- /dev/null +++ b/GanjooRazor/Areas/Admin/Pages/Roles.cshtml @@ -0,0 +1,183 @@ +@page +@model GanjooRazor.Areas.Admin.Pages.RolesModel +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@{ + Layout = "_AdminLayout"; + ViewData["Title"] = "مدیریت نقش‌ها"; +} + +
+

@ViewData["Title"]

+
+ +@if (!string.IsNullOrEmpty(Model.LastError)) +{ +
@Model.LastError
+} + +
+

نقش جدید

+
+ + + ➕ ایجاد +
+
+ + + +@if (Model.Roles != null && Model.Roles.Length > 0) +{ + + + + + + + @foreach (var role in Model.Roles) + { + + + + + + + + + } +
نامتوضیحات#
@role.Name@role.Description + 🔑 + 🗑 +
+} +else +{ + +} diff --git a/GanjooRazor/Areas/Admin/Pages/Roles.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/Roles.cshtml.cs new file mode 100644 index 00000000..97a4e7b0 --- /dev/null +++ b/GanjooRazor/Areas/Admin/Pages/Roles.cshtml.cs @@ -0,0 +1,132 @@ +using GanjooRazor.Utils; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Newtonsoft.Json; +using RSecurityBackend.Models.Auth.Db; +using RSecurityBackend.Models.Auth.Memory; +using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace GanjooRazor.Areas.Admin.Pages +{ + [IgnoreAntiforgeryToken(Order = 1001)] + public class RolesModel : PageModel + { + /// + /// Last Error + /// + public string LastError { get; set; } + + public RAppRole[] Roles { get; set; } + + public async Task OnGetAsync() + { + if (string.IsNullOrEmpty(Request.Cookies["Token"])) + return Redirect("/"); + + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + { + var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles"); + if (response.IsSuccessStatusCode) + { + Roles = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + } + else + { + LastError = await response.Content.ReadAsStringAsync(); + } + } + else + { + LastError = "لطفاً از گنجور خارج و مجددا به آن وارد شوید."; + } + } + + return Page(); + } + + /// + /// create a new role - ?handler=NewRole + /// + public async Task OnPostNewRoleAsync(string name, string description) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var newRole = new { name = name, description = description }; + var response = await secureClient.PostAsync($"{APIRoot.Url}/api/roles", new StringContent(JsonConvert.SerializeObject(newRole), Encoding.UTF8, "application/json")); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + + /// + /// delete a role - ?handler=Role (DELETE) + /// + public async Task OnDeleteRoleAsync(string roleName) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/roles/{roleName}"); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + + /// + /// securable items tree annotated with this role's current permission status - ?handler=Permissions&roleName=... + /// + public async Task OnGetPermissionsAsync(string roleName) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles/permissions/{roleName}"); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + var items = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + return new JsonResult(items); + } + } + + /// + /// save this role's permissions - ?handler=Permissions&roleName=... (PUT) + /// + public async Task OnPutPermissionsAsync(string roleName) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + string body; + using (var reader = new StreamReader(Request.Body)) + { + body = await reader.ReadToEndAsync(); + } + + var response = await secureClient.PutAsync($"{APIRoot.Url}/api/roles/permissions/{roleName}", new StringContent(body, Encoding.UTF8, "application/json")); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + } +} diff --git a/GanjooRazor/Areas/Admin/Pages/Users.cshtml b/GanjooRazor/Areas/Admin/Pages/Users.cshtml new file mode 100644 index 00000000..84b3e50f --- /dev/null +++ b/GanjooRazor/Areas/Admin/Pages/Users.cshtml @@ -0,0 +1,216 @@ +@page +@model GanjooRazor.Areas.Admin.Pages.UsersModel +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@{ + Layout = "_AdminLayout"; + ViewData["Title"] = "مدیریت کاربران"; + ViewData["WideShell"] = true; +} + +
+

@ViewData["Title"]

+
+ +@if (!string.IsNullOrEmpty(Model.LastError)) +{ +
@Model.LastError
+} + +
+
+ + + +
+
+ + + +@if (Model.Users != null && Model.Users.Length > 0) +{ + + + + + + + + + @foreach (var user in Model.Users) + { + + + + + + + + + + + } +
پست الکترونیکینامنام خانوادگیفعال#
@user.Email@user.FirstName@user.SurName + @if (user.Status == RSecurityBackend.Models.Auth.Db.RAppUserStatus.Active) + { + + } + else + { + + } + + 🎭 + 👁️ +
+} +else +{ + +} + + diff --git a/GanjooRazor/Areas/Admin/Pages/Users.cshtml.cs b/GanjooRazor/Areas/Admin/Pages/Users.cshtml.cs new file mode 100644 index 00000000..5651ebfc --- /dev/null +++ b/GanjooRazor/Areas/Admin/Pages/Users.cshtml.cs @@ -0,0 +1,216 @@ +using GanjooRazor.Utils; +using GSpotifyProxy.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Newtonsoft.Json; +using RSecurityBackend.Models.Auth.Db; +using RSecurityBackend.Models.Auth.ViewModels; +using RSecurityBackend.Models.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace GanjooRazor.Areas.Admin.Pages +{ + [IgnoreAntiforgeryToken(Order = 1001)] + public class UsersModel : PageModel + { + /// + /// Last Error + /// + public string LastError { get; set; } + + public PublicRAppUser[] Users { get; set; } + + public PaginationMetadata Paging { get; set; } + + public List PaginationLinks { get; set; } = new List(); + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 15; + + public string FilterByEmail { get; set; } + + public async Task OnGetAsync(int pageNumber = 1, int pageSize = 15, string filterByEmail = null) + { + if (string.IsNullOrEmpty(Request.Cookies["Token"])) + return Redirect("/"); + + PageNumber = pageNumber < 1 ? 1 : pageNumber; + PageSize = pageSize; + FilterByEmail = filterByEmail; + + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + { + var url = $"{APIRoot.Url}/api/users?PageNumber={PageNumber}&PageSize={PageSize}"; + if (!string.IsNullOrEmpty(FilterByEmail)) + url += $"&filterByEmail={Uri.EscapeDataString(FilterByEmail)}"; + + var response = await secureClient.GetAsync(url); + if (response.IsSuccessStatusCode) + { + Users = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + if (response.Headers.TryGetValues("paging-headers", out var values)) + { + Paging = JsonConvert.DeserializeObject(values.FirstOrDefault()); + if (Paging != null) + { + for (int p = 1; p <= Paging.totalPages; p++) + { + var pageUrl = $"?pageNumber={p}&pageSize={PageSize}"; + if (!string.IsNullOrEmpty(FilterByEmail)) + pageUrl += $"&filterByEmail={Uri.EscapeDataString(FilterByEmail)}"; + PaginationLinks.Add(new NameIdUrlImage() + { + Name = p.ToString(), + Url = p == PageNumber ? "" : pageUrl + }); + } + } + } + } + else + { + LastError = await response.Content.ReadAsStringAsync(); + } + } + else + { + LastError = "لطفاً از گنجور خارج و مجددا به آن وارد شوید."; + } + } + + return Page(); + } + + /// + /// role names for a specific user - ?handler=Roles&id=... + /// + public async Task OnGetRolesAsync(Guid id) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}/roles"); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + var roles = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + return new JsonResult(roles); + } + } + + /// + /// all defined role names, for populating the "add role" picker - ?handler=AllRoles + /// + public async Task OnGetAllRolesAsync() + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.GetAsync($"{APIRoot.Url}/api/roles"); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + var roles = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()); + return new JsonResult(roles.Select(r => r.Name).ToArray()); + } + } + + /// + /// add a role to a user - ?handler=AddRole + /// + public async Task OnPostAddRoleAsync(Guid id, string role) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.PostAsync($"{APIRoot.Url}/api/users/{id}/roles/{Uri.EscapeDataString(role)}", null); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + + /// + /// remove a role from a user - ?handler=Role (DELETE) + /// + public async Task OnDeleteRoleAsync(Guid id, string role) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var response = await secureClient.DeleteAsync($"{APIRoot.Url}/api/users/{id}/roles/{Uri.EscapeDataString(role)}"); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + + /// + /// toggle a user's active/inactive status - ?handler=ToggleStatus + /// + public async Task OnPostToggleStatusAsync(Guid id, bool active) + { + using (HttpClient secureClient = new HttpClient(new GanjoorReloginHandler(Request, Response))) + { + if (!await GanjoorSessionChecker.PrepareClient(secureClient, Request, Response)) + return BadRequest("لطفاً از گنجور خارج و مجددا به آن وارد شوید."); + + var userInfoResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}"); + if (!userInfoResponse.IsSuccessStatusCode) + return BadRequest(await userInfoResponse.Content.ReadAsStringAsync()); + + var userInfo = JsonConvert.DeserializeObject(await userInfoResponse.Content.ReadAsStringAsync()); + + // ModifyUser rejects the request unless IsAdmin exactly matches the user's actual + // current admin-role membership (this endpoint cannot itself change admin status), + // so we need their real membership, not a guess. + var rolesResponse = await secureClient.GetAsync($"{APIRoot.Url}/api/users/{id}/roles"); + if (!rolesResponse.IsSuccessStatusCode) + return BadRequest(await rolesResponse.Content.ReadAsStringAsync()); + var currentRoles = JsonConvert.DeserializeObject(await rolesResponse.Content.ReadAsStringAsync()); + bool isCurrentlyAdmin = currentRoles.Contains("Administrator"); + + var updateModel = new RegisterRAppUser() + { + Password = "", + IsAdmin = isCurrentlyAdmin, + Id = userInfo.Id, + Username = userInfo.Username, + Email = userInfo.Email, + PhoneNumber = userInfo.PhoneNumber, + FirstName = userInfo.FirstName, + SurName = userInfo.SurName, + Status = active ? RAppUserStatus.Active : RAppUserStatus.Inactive, + RImageId = userInfo.RImageId, + NickName = userInfo.NickName, + Bio = userInfo.Bio, + Website = userInfo.Website + }; + + var response = await secureClient.PutAsync($"{APIRoot.Url}/api/users/{id}", new StringContent(JsonConvert.SerializeObject(updateModel), Encoding.UTF8, "application/json")); + if (!response.IsSuccessStatusCode) + return BadRequest(await response.Content.ReadAsStringAsync()); + + return new JsonResult(true); + } + } + } +} diff --git a/GanjooRazor/Pages/Shared/_AdminLayout.cshtml b/GanjooRazor/Pages/Shared/_AdminLayout.cshtml index ee990074..2c631c56 100644 --- a/GanjooRazor/Pages/Shared/_AdminLayout.cshtml +++ b/GanjooRazor/Pages/Shared/_AdminLayout.cshtml @@ -498,6 +498,14 @@ + +