Skip to content

Commit bbff3da

Browse files
akoclaude
andcommitted
fix: SHOW ACCESS ON Entity respects --json flag (issue #134)
Converts showAccessOnEntity to emit a TableResult when --json is set. Each access rule becomes one row: Rule, Roles, Rights, DefaultMemberAccess, MemberAccess (attr:rights pairs), XPath. Also refactors the repeated rights/roles/memberName logic into local closures shared by both the JSON and text output paths, removing duplication from the original text path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 02f5754 commit bbff3da

1 file changed

Lines changed: 61 additions & 35 deletions

File tree

mdl/executor/cmd_security.go

Lines changed: 61 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -194,33 +194,26 @@ func showAccessOnEntity(ctx *ExecContext, name *ast.QualifiedName) error {
194194
return mdlerrors.NewNotFound("entity", name.String())
195195
}
196196

197-
if len(entity.AccessRules) == 0 {
198-
fmt.Fprintf(ctx.Output, "No access rules on %s\n", name)
199-
return nil
200-
}
201-
202-
// Build attribute name map
197+
// Build attribute name map (shared by both output paths)
203198
attrNames := make(map[string]string)
204199
for _, attr := range entity.Attributes {
205200
attrNames[string(attr.ID)] = attr.Name
206201
}
207202

208-
fmt.Fprintf(ctx.Output, "Access rules for %s.%s:\n\n", name.Module, name.Name)
209-
210-
for i, rule := range entity.AccessRules {
211-
// Show roles
212-
var roleStrs []string
213-
for _, rn := range rule.ModuleRoleNames {
214-
roleStrs = append(roleStrs, rn)
203+
// ruleRoles returns the role name list for a rule.
204+
ruleRoles := func(rule *domainmodel.AccessRule) []string {
205+
if len(rule.ModuleRoleNames) > 0 {
206+
return rule.ModuleRoleNames
215207
}
216-
if len(roleStrs) == 0 {
217-
for _, rid := range rule.ModuleRoles {
218-
roleStrs = append(roleStrs, string(rid))
219-
}
208+
var out []string
209+
for _, rid := range rule.ModuleRoles {
210+
out = append(out, string(rid))
220211
}
221-
fmt.Fprintf(ctx.Output, "Rule %d: %s\n", i+1, strings.Join(roleStrs, ", "))
212+
return out
213+
}
222214

223-
// Show CRUD rights (READ/WRITE inferred from DefaultMemberAccessRights + MemberAccesses)
215+
// ruleRights computes CRUD rights for a rule.
216+
ruleRights := func(rule *domainmodel.AccessRule) []string {
224217
var rights []string
225218
if rule.AllowCreate {
226219
rights = append(rights, "CREATE")
@@ -245,30 +238,63 @@ func showAccessOnEntity(ctx *ExecContext, name *ast.QualifiedName) error {
245238
if rule.AllowDelete {
246239
rights = append(rights, "DELETE")
247240
}
248-
fmt.Fprintf(ctx.Output, " Rights: %s\n", strings.Join(rights, ", "))
241+
return rights
242+
}
243+
244+
// memberName resolves display name for a MemberAccess entry.
245+
memberName := func(ma *domainmodel.MemberAccess) string {
246+
if ma.AttributeName != "" {
247+
return ma.AttributeName
248+
}
249+
if ma.AssociationName != "" {
250+
return ma.AssociationName
251+
}
252+
if an, ok := attrNames[string(ma.AttributeID)]; ok {
253+
return an
254+
}
255+
return string(ma.AttributeID)
256+
}
257+
258+
if ctx.Format == FormatJSON {
259+
result := &TableResult{
260+
Columns: []string{"Rule", "Roles", "Rights", "DefaultMemberAccess", "MemberAccess", "XPath"},
261+
}
262+
for i, rule := range entity.AccessRules {
263+
var memberParts []string
264+
for _, ma := range rule.MemberAccesses {
265+
memberParts = append(memberParts, memberName(ma)+":"+string(ma.AccessRights))
266+
}
267+
result.Rows = append(result.Rows, []any{
268+
i + 1,
269+
strings.Join(ruleRoles(rule), ", "),
270+
strings.Join(ruleRights(rule), ", "),
271+
string(rule.DefaultMemberAccessRights),
272+
strings.Join(memberParts, ", "),
273+
rule.XPathConstraint,
274+
})
275+
}
276+
return writeResult(ctx, result)
277+
}
278+
279+
if len(entity.AccessRules) == 0 {
280+
fmt.Fprintf(ctx.Output, "No access rules on %s\n", name)
281+
return nil
282+
}
283+
284+
fmt.Fprintf(ctx.Output, "Access rules for %s.%s:\n\n", name.Module, name.Name)
285+
286+
for i, rule := range entity.AccessRules {
287+
fmt.Fprintf(ctx.Output, "Rule %d: %s\n", i+1, strings.Join(ruleRoles(rule), ", "))
288+
fmt.Fprintf(ctx.Output, " Rights: %s\n", strings.Join(ruleRights(rule), ", "))
249289

250-
// Show default member access
251290
if rule.DefaultMemberAccessRights != "" {
252291
fmt.Fprintf(ctx.Output, " Default member access: %s\n", rule.DefaultMemberAccessRights)
253292
}
254293

255-
// Show member-level access
256294
for _, ma := range rule.MemberAccesses {
257-
memberName := ma.AttributeName
258-
if memberName == "" {
259-
memberName = ma.AssociationName
260-
}
261-
if memberName == "" {
262-
if an, ok := attrNames[string(ma.AttributeID)]; ok {
263-
memberName = an
264-
} else {
265-
memberName = string(ma.AttributeID)
266-
}
267-
}
268-
fmt.Fprintf(ctx.Output, " %s: %s\n", memberName, ma.AccessRights)
295+
fmt.Fprintf(ctx.Output, " %s: %s\n", memberName(ma), ma.AccessRights)
269296
}
270297

271-
// Show XPath constraint
272298
if rule.XPathConstraint != "" {
273299
fmt.Fprintf(ctx.Output, " WHERE '%s'\n", rule.XPathConstraint)
274300
}

0 commit comments

Comments
 (0)