forked from gnolang/gno
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add p/nestedpkg (gnolang#2342)
This package demonstrates a new access control pattern relying on package paths. It considers that the gnolang#1107 feature is already implemented to rely on the built-in security. You can see an ongoing usage in gnolang#2334. --------- Signed-off-by: moul <94029+moul@users.noreply.github.com>
- Loading branch information
Showing
5 changed files
with
179 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module nestedpkg | ||
|
||
require gno.land/r/demo/tests v0.0.0-latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// Package nestedpkg provides helpers for package-path based access control. | ||
// It is useful for upgrade patterns relying on namespaces. | ||
package nestedpkg | ||
|
||
import ( | ||
"std" | ||
"strings" | ||
) | ||
|
||
// IsCallerSubPath checks if the caller realm is located in a subfolder of the current realm. | ||
func IsCallerSubPath() bool { | ||
var ( | ||
cur = std.CurrentRealm().PkgPath() + "/" | ||
prev = std.PrevRealm().PkgPath() + "/" | ||
) | ||
return strings.HasPrefix(prev, cur) | ||
} | ||
|
||
// AssertCallerIsSubPath panics if IsCallerSubPath returns false. | ||
func AssertCallerIsSubPath() { | ||
var ( | ||
cur = std.CurrentRealm().PkgPath() + "/" | ||
prev = std.PrevRealm().PkgPath() + "/" | ||
) | ||
if !strings.HasPrefix(prev, cur) { | ||
panic("call restricted to nested packages. current realm is " + cur + ", previous realm is " + prev) | ||
} | ||
} | ||
|
||
// IsCallerParentPath checks if the caller realm is located in a parent location of the current realm. | ||
func IsCallerParentPath() bool { | ||
var ( | ||
cur = std.CurrentRealm().PkgPath() + "/" | ||
prev = std.PrevRealm().PkgPath() + "/" | ||
) | ||
return strings.HasPrefix(cur, prev) | ||
} | ||
|
||
// AssertCallerIsParentPath panics if IsCallerParentPath returns false. | ||
func AssertCallerIsParentPath() { | ||
var ( | ||
cur = std.CurrentRealm().PkgPath() + "/" | ||
prev = std.PrevRealm().PkgPath() + "/" | ||
) | ||
if !strings.HasPrefix(cur, prev) { | ||
panic("call restricted to parent packages. current realm is " + cur + ", previous realm is " + prev) | ||
} | ||
} | ||
|
||
// IsSameNamespace checks if the caller realm and the current realm are in the same namespace. | ||
func IsSameNamespace() bool { | ||
var ( | ||
cur = nsFromPath(std.CurrentRealm().PkgPath()) + "/" | ||
prev = nsFromPath(std.PrevRealm().PkgPath()) + "/" | ||
) | ||
return cur == prev | ||
} | ||
|
||
// AssertIsSameNamespace panics if IsSameNamespace returns false. | ||
func AssertIsSameNamespace() { | ||
var ( | ||
cur = nsFromPath(std.CurrentRealm().PkgPath()) + "/" | ||
prev = nsFromPath(std.PrevRealm().PkgPath()) + "/" | ||
) | ||
if cur != prev { | ||
panic("call restricted to packages from the same namespace. current realm is " + cur + ", previous realm is " + prev) | ||
} | ||
} | ||
|
||
// nsFromPath extracts the namespace from a package path. | ||
func nsFromPath(pkgpath string) string { | ||
parts := strings.Split(pkgpath, "/") | ||
|
||
// Specifically for gno.land, potential paths are in the form of DOMAIN/r/NAMESPACE/... | ||
// XXX: Consider extra checks. | ||
// XXX: Support non gno.land domains, where p/ and r/ won't be enforced. | ||
if len(parts) >= 3 { | ||
return parts[2] | ||
} | ||
return "" | ||
} | ||
|
||
// XXX: Consider adding IsCallerDirectlySubPath | ||
// XXX: Consider adding IsCallerDirectlyParentPath |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package nestedpkg | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/r/demo/tests" | ||
) | ||
|
||
func TestPackage(t *testing.T) { | ||
// direct child | ||
cur := "gno.land/r/demo/tests/foo" | ||
std.TestSetRealm(std.NewCodeRealm(cur)) | ||
if !tests.IsCallerSubPath() { | ||
t.Errorf(cur + " should be a sub path") | ||
} | ||
if tests.IsCallerParentPath() { | ||
t.Errorf(cur + " should not be a parent path") | ||
} | ||
if !tests.HasCallerSameNamespace() { | ||
t.Errorf(cur + " should be from the same namespace") | ||
} | ||
|
||
// grand-grand-child | ||
cur = "gno.land/r/demo/tests/foo/bar/baz" | ||
std.TestSetRealm(std.NewCodeRealm(cur)) | ||
if !tests.IsCallerSubPath() { | ||
t.Errorf(cur + " should be a sub path") | ||
} | ||
if tests.IsCallerParentPath() { | ||
t.Errorf(cur + " should not be a parent path") | ||
} | ||
if !tests.HasCallerSameNamespace() { | ||
t.Errorf(cur + " should be from the same namespace") | ||
} | ||
|
||
// direct parent | ||
cur = "gno.land/r/demo" | ||
std.TestSetRealm(std.NewCodeRealm(cur)) | ||
if tests.IsCallerSubPath() { | ||
t.Errorf(cur + " should not be a sub path") | ||
} | ||
if !tests.IsCallerParentPath() { | ||
t.Errorf(cur + " should be a parent path") | ||
} | ||
if !tests.HasCallerSameNamespace() { | ||
t.Errorf(cur + " should be from the same namespace") | ||
} | ||
|
||
// fake parent (prefix) | ||
cur = "gno.land/r/dem" | ||
std.TestSetRealm(std.NewCodeRealm(cur)) | ||
if tests.IsCallerSubPath() { | ||
t.Errorf(cur + " should not be a sub path") | ||
} | ||
if tests.IsCallerParentPath() { | ||
t.Errorf(cur + " should not be a parent path") | ||
} | ||
if tests.HasCallerSameNamespace() { | ||
t.Errorf(cur + " should not be from the same namespace") | ||
} | ||
|
||
// different namespace | ||
cur = "gno.land/r/foo" | ||
std.TestSetRealm(std.NewCodeRealm(cur)) | ||
if tests.IsCallerSubPath() { | ||
t.Errorf(cur + " should not be a sub path") | ||
} | ||
if tests.IsCallerParentPath() { | ||
t.Errorf(cur + " should not be a parent path") | ||
} | ||
if tests.HasCallerSameNamespace() { | ||
t.Errorf(cur + " should not be from the same namespace") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
module gno.land/r/demo/tests | ||
|
||
require gno.land/r/demo/tests/subtests v0.0.0-latest | ||
require ( | ||
gno.land/p/demo/nestedpkg v0.0.0-latest | ||
gno.land/r/demo/tests/subtests v0.0.0-latest | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters