diff --git a/.gitea/CODEOWNERS b/.gitea/CODEOWNERS new file mode 100644 index 0000000..ae1f67b --- /dev/null +++ b/.gitea/CODEOWNERS @@ -0,0 +1 @@ +* @go/CoreDevelopers @go/Agents diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c1c1b2..5ef31f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ All notable changes to this module will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.0] — 2026-05-08 + +### Added + +- `MaxPermission Permission = 62` — exported constant that makes the valid bit range + explicit in the API; applications can use it in validation code and it is referenced + in the `Permission` type godoc + +### Changed + +- `Permission` type godoc updated to reference `MaxPermission` and document that + values outside `[0, MaxPermission]` are silently ignored +- `PermissionProvider` godoc updated to document that audit logging of permission + checks is out of scope for this package — log denials and grants inside + PermissionProvider implementations or in the middleware layer + +### Unchanged + +Identity, PermissionMask (Has, Grant), context helpers (SetInContext, FromContext), +and the PermissionProvider interface are API-compatible with v0.9.0. + +[1.0.0]: https://code.nochebuena.dev/go/rbac/releases/tag/v1.0.0 + ## [0.9.0] - 2026-03-18 ### Added diff --git a/permission.go b/permission.go index e7d509b..21fefd5 100644 --- a/permission.go +++ b/permission.go @@ -11,8 +11,15 @@ package rbac // ) // // The zero value (0) is a valid permission representing the first bit. +// Valid positions are 0 through [MaxPermission] (62); values outside that +// range are silently ignored by [PermissionMask.Has] and [PermissionMask.Grant]. type Permission int64 +// MaxPermission is the highest valid bit position (62). +// Permission constants defined by the application must be in the range +// [0, MaxPermission]. Bit 63 is reserved for the sign bit of the underlying int64. +const MaxPermission Permission = 62 + // PermissionMask is a resolved bit-mask for a user on a specific resource. // It is returned by [PermissionProvider.ResolveMask] and checked with [PermissionMask.Has]. type PermissionMask int64 diff --git a/permission_test.go b/permission_test.go index 1ccb93d..ac96380 100644 --- a/permission_test.go +++ b/permission_test.go @@ -93,3 +93,19 @@ func TestPermissionMask_Grant_DoesNotMutateReceiver(t *testing.T) { t.Error("Grant mutated the receiver; PermissionMask must be a value type") } } + +func TestMaxPermission(t *testing.T) { + if MaxPermission != 62 { + t.Errorf("MaxPermission = %d, want 62", MaxPermission) + } + // MaxPermission must be usable as a valid bit position. + mask := PermissionMask(0).Grant(MaxPermission) + if !mask.Has(MaxPermission) { + t.Error("MaxPermission bit not set after Grant") + } + // One past the limit must be rejected. + mask2 := PermissionMask(0).Grant(MaxPermission + 1) + if mask2 != 0 { + t.Error("Grant(MaxPermission+1) must return the original mask unchanged") + } +} diff --git a/provider.go b/provider.go index 65d1f15..c693851 100644 --- a/provider.go +++ b/provider.go @@ -11,6 +11,10 @@ import "context" // The resource string identifies what is being accessed (e.g. "orders", // "invoices"). Its meaning is defined by the application. // +// Audit logging of permission checks is out of scope for this package. +// Log denials and grants inside your PermissionProvider implementation or in +// the middleware layer that calls it. +// // Example in-memory implementation for tests: // // type staticProvider struct {