Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/WalkingTec.Mvvm.Mvc/_AnalysisController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,12 @@ public IActionResult ListSavedQueries([FromQuery] string listVmType)
if (string.IsNullOrWhiteSpace(listVmType))
return BadRequest("listVmType is required.");

Type vmType;
try { vmType = _registry.Resolve(listVmType); }
catch (AnalysisVmNotFoundException ex) { return NotFound(ex.Message); }
catch (InvalidOperationException ex) { return BadRequest(ex.Message); }
if (!CheckAccess(vmType)) return Forbid();

var userCode = Wtm?.LoginUserInfo?.ITCode ?? string.Empty;

var rows = Wtm!.DC.Set<AnalysisSavedQuery>()
Expand Down Expand Up @@ -337,8 +343,11 @@ public IActionResult SaveQuery([FromBody] SaveQueryRequest? req)
if (string.IsNullOrWhiteSpace(req.Name)) return BadRequest("查詢名稱不可為空。");
if (string.IsNullOrWhiteSpace(req.Config?.ListVmType)) return BadRequest("Config.ListVmType is required.");

try { _registry.Resolve(req.Config.ListVmType); }
Type vmType;
try { vmType = _registry.Resolve(req.Config.ListVmType); }
catch (AnalysisVmNotFoundException ex) { return NotFound(ex.Message); }
catch (InvalidOperationException ex) { return BadRequest(ex.Message); }
if (!CheckAccess(vmType)) return Forbid();

var userCode = Wtm?.LoginUserInfo?.ITCode ?? string.Empty;
var configJson = JsonSerializer.Serialize(req.Config, _camelCase);
Expand Down Expand Up @@ -385,6 +394,11 @@ public IActionResult GetSavedQuery(Guid id)
catch (JsonException) { return BadRequest("儲存的查詢格式無效。"); }

if (config == null) return BadRequest("儲存的查詢格式無效。");
Type vmType;
try { vmType = _registry.Resolve(config.ListVmType); }
catch (AnalysisVmNotFoundException ex) { return NotFound(ex.Message); }
catch (InvalidOperationException ex) { return BadRequest(ex.Message); }
if (!CheckAccess(vmType)) return Forbid();

return new JsonResult(config, _camelCase);
}
Expand Down
54 changes: 54 additions & 0 deletions test/WalkingTec.Mvvm.Core.Test/Analysis/AnalysisControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2496,6 +2496,36 @@ public void ListSavedQueries_returns_public_and_owned_queries()
Assert.IsTrue(names.Contains("B_Public"), "UserA 應能看到 UserB 的 Public 查詢");
}

[TestMethod]
public void ListSavedQueries_returns_403_when_user_lacks_required_role()
{
var controller = CreateControllerWithRoles("Viewer");
var vmType = typeof(RestrictedSaleListVM).FullName;

var result = controller.ListSavedQueries(vmType);
Assert.IsInstanceOfType(result, typeof(ForbidResult));
}

[TestMethod]
public void SaveQuery_returns_403_when_user_lacks_required_role()
{
var controller = CreateControllerWithRoles("Viewer");
var req = new SaveQueryRequest
{
Name = "Restricted",
IsPublic = false,
Config = new AnalysisQueryRequest
{
ListVmType = typeof(RestrictedSaleListVM).FullName,
Dimensions = new List<string> { "Region" },
Measures = new List<MeasureRequest> { new MeasureRequest { Field = "Amount", Func = AggregateFunc.Sum } }
}
};

var result = controller.SaveQuery(req);
Assert.IsInstanceOfType(result, typeof(ForbidResult));
}

[TestMethod]
public void GetSavedQuery_returns_config_for_authorized_user()
{
Expand Down Expand Up @@ -2540,6 +2570,30 @@ public void GetSavedQuery_returns_403_for_unauthorized_user()
Assert.IsNotNull(result, "存取他人的私有查詢應回傳 403 Forbid");
}

[TestMethod]
public void GetSavedQuery_returns_403_when_public_query_vm_is_role_restricted()
{
var controller = CreateControllerWithRoles("Viewer");
controller.Wtm.LoginUserInfo.ITCode = "userA";
var id = Guid.NewGuid();
var vmType = typeof(RestrictedSaleListVM).FullName;
controller.Wtm.DC.Set<AnalysisSavedQuery>().Add(
new AnalysisSavedQuery
{
ID = id,
Name = "RestrictedPublic",
ListVmType = vmType,
OwnerCode = "userB",
IsPublic = true,
ConfigJson = "{\"listVmType\":\"" + vmType + "\"}"
}
);
controller.Wtm.DC.SaveChanges();

var result = controller.GetSavedQuery(id);
Assert.IsInstanceOfType(result, typeof(ForbidResult));
}

[TestMethod]
public async Task DeleteSavedQuery_removes_record_if_owner()
{
Expand Down
Loading