Skip to content

Commit

Permalink
Turn scr into list and add locale for time ranges (#94)
Browse files Browse the repository at this point in the history
Signed-off-by: Matthias Hanel <mh@synadia.com>
  • Loading branch information
matthiashanel committed Aug 27, 2020
1 parent f895b3f commit 292806f
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 35 deletions.
5 changes: 4 additions & 1 deletion v2/decoder_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ func TestMigrateUser(t *testing.T) {
uc := v1jwt.NewUserClaims(upk)
uc.Name = "U"
uc.Audience = "Audience"

uc.Src = " 127.0.0.1/1 , 127.0.0.1/2 "
now := time.Now()
uc.NotBefore = now.Unix()
e := now.Add(time.Hour)
Expand Down Expand Up @@ -214,6 +214,9 @@ func TestMigrateUser(t *testing.T) {
AssertTrue(uc2.Limits.Payload == NoLimit, t)
AssertTrue(uc2.Limits.Subs == NoLimit, t)
AssertTrue(uc2.Limits.Data == NoLimit, t)
AssertTrue(len(uc2.Src) == 2, t)
AssertTrue(uc2.Src.Contains("127.0.0.1/1"), t)
AssertTrue(uc2.Src.Contains("127.0.0.1/2"), t)

equalUsers(t, uc, uc2)
}
Expand Down
71 changes: 49 additions & 22 deletions v2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,6 @@ func (s Subject) IsContainedIn(other Subject) bool {
return true
}

// NamedSubject is the combination of a subject and a name for it
type NamedSubject struct {
Name string `json:"name,omitempty"`
Subject Subject `json:"subject,omitempty"`
}

// Validate checks the subject
func (ns *NamedSubject) Validate(vr *ValidationResults) {
ns.Subject.Validate(vr)
}

// TimeRange is used to represent a start and end time
type TimeRange struct {
Start string `json:"start,omitempty"`
Expand Down Expand Up @@ -169,12 +158,13 @@ func (tr *TimeRange) Validate(vr *ValidationResults) {

// Src is a comma separated list of CIDR specifications
type UserLimits struct {
Src string `json:"src,omitempty"`
Times []TimeRange `json:"times,omitempty"`
Src CIDRList `json:"src,omitempty"`
Times []TimeRange `json:"times,omitempty"`
Locale string `json:"times_location,omitempty"`
}

func (u *UserLimits) IsUnlimited() bool {
return u.Src == "" && len(u.Times) == 0
return len(u.Src) == 0 && len(u.Times) == 0
}

// Limits are used to control acccess for users and importing accounts
Expand All @@ -189,11 +179,8 @@ func (l *Limits) IsUnlimited() bool {

// Validate checks the values in a limit struct
func (l *Limits) Validate(vr *ValidationResults) {
if l.Src != "" {
elements := strings.Split(l.Src, ",")

for _, cidr := range elements {
cidr = strings.TrimSpace(cidr)
if len(l.Src) != 0 {
for _, cidr := range l.Src {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil || ipNet == nil {
vr.AddError("invalid cidr %q in user src limits", cidr)
Expand All @@ -206,6 +193,12 @@ func (l *Limits) Validate(vr *ValidationResults) {
t.Validate(vr)
}
}

if l.Locale != "" {
if _, err := time.LoadLocation(l.Locale); err != nil {
vr.AddError("could not parse iana time zone by name: %v", err)
}
}
}

// Permission defines allow/deny subjects
Expand Down Expand Up @@ -291,7 +284,7 @@ type TagList []string

// Contains returns true if the list contains the tags
func (u *TagList) Contains(p string) bool {
p = strings.ToLower(p)
p = strings.ToLower(strings.TrimSpace(p))
for _, t := range *u {
if t == p {
return true
Expand All @@ -303,7 +296,7 @@ func (u *TagList) Contains(p string) bool {
// Add appends 1 or more tags to a list
func (u *TagList) Add(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
v = strings.ToLower(strings.TrimSpace(v))
if !u.Contains(v) && v != "" {
*u = append(*u, v)
}
Expand All @@ -313,7 +306,7 @@ func (u *TagList) Add(p ...string) {
// Remove removes 1 or more tags from a list
func (u *TagList) Remove(p ...string) {
for _, v := range p {
v = strings.ToLower(v)
v = strings.ToLower(strings.TrimSpace(v))
for i, t := range *u {
if t == v {
a := *u
Expand All @@ -324,6 +317,40 @@ func (u *TagList) Remove(p ...string) {
}
}

type CIDRList TagList

func (c *CIDRList) Contains(p string) bool {
return (*TagList)(c).Contains(p)
}

func (c *CIDRList) Add(p ...string) {
(*TagList)(c).Add(p...)
}

func (c *CIDRList) Remove(p ...string) {
(*TagList)(c).Remove(p...)
}

func (c *CIDRList) Set(values string) {
*c = CIDRList{}
c.Add(strings.Split(strings.ToLower(values), ",")...)
}

func (c *CIDRList) UnmarshalJSON(body []byte) (err error) {
// parse either as array of strings or comma separate list
var request []string
var list string
if err := json.Unmarshal(body, &request); err == nil {
*c = request
return nil
} else if err := json.Unmarshal(body, &list); err == nil {
c.Set(list)
return nil
} else {
return err
}
}

// Identity is used to associate an account or operator with a real entity
type Identity struct {
ID string `json:"id,omitempty"`
Expand Down
2 changes: 1 addition & 1 deletion v2/user_claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func NewUserClaims(subject string) *UserClaims {
c := &UserClaims{}
c.Subject = subject
c.Limits = Limits{
UserLimits{"", nil},
UserLimits{CIDRList{}, nil, ""},
NatsLimits{NoLimit, NoLimit, NoLimit},
}
return c
Expand Down
43 changes: 32 additions & 11 deletions v2/user_claims_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func TestUserValidation(t *testing.T) {
Expires: 50 * time.Minute,
}
uc.Limits.Payload = 10
uc.Limits.Src = "192.0.2.0/24"
uc.Limits.Src.Set("192.0.2.0/24")
uc.Limits.Times = []TimeRange{
{
Start: "01:15:00",
Expand All @@ -176,6 +176,7 @@ func TestUserValidation(t *testing.T) {
End: "09:15:00",
},
}
uc.Limits.Locale = "Europe/Berlin"

vr := CreateValidationResults()
uc.Validate(vr)
Expand All @@ -184,7 +185,7 @@ func TestUserValidation(t *testing.T) {
t.Error("valid user permissions should be valid")
}

uc.Limits.Src = "hello world"
uc.Limits.Src.Set("hello world")
vr = CreateValidationResults()
uc.Validate(vr)

Expand All @@ -193,7 +194,7 @@ func TestUserValidation(t *testing.T) {
}

uc.Limits.Payload = 10
uc.Limits.Src = "hello world"
uc.Limits.Src.Set("hello world")
vr = CreateValidationResults()
uc.Validate(vr)

Expand All @@ -205,14 +206,26 @@ func TestUserValidation(t *testing.T) {
Start: "hello",
End: "03:15:00",
}
uc.Limits.Src = "192.0.2.0/24"
uc.Limits.Src.Set("192.0.2.0/24")
uc.Limits.Times = append(uc.Limits.Times, tr)
vr = CreateValidationResults()
uc.Validate(vr)

if vr.IsEmpty() || len(vr.Issues) != 1 || !vr.IsBlocking(true) {
t.Error("bad limit should be invalid")
}

uc.Limits.Times = []TimeRange{{
Start: "02:15:00",
End: "03:15:00",
}}
uc.Limits.Locale = "foo"
vr = CreateValidationResults()
uc.Validate(vr)

if vr.IsEmpty() || len(vr.Issues) != 1 || !vr.IsBlocking(true) {
t.Error("bad location should be invalid")
}
}

func TestUserAccountID(t *testing.T) {
Expand Down Expand Up @@ -273,55 +286,63 @@ func TestSourceNetworkValidation(t *testing.T) {
ukp := createUserNKey(t)
uc := NewUserClaims(publicKey(ukp, t))

uc.Limits.Src = "192.0.2.0/24"
uc.Limits.Src = CIDRList{"192.0.2.0/24"}
vr := CreateValidationResults()
uc.Validate(vr)

if !vr.IsEmpty() {
t.Error("limits should be valid")
}

uc.Limits.Src = "192.0.2.1/1"
uc.Limits.Src = CIDRList{"192.0.2.0/24"}
vr = CreateValidationResults()
uc.Validate(vr)

if !vr.IsEmpty() {
t.Error("limits should be valid")
}

uc.Limits.Src = CIDRList{"192.0.2.0/24", "2001:db8:a0b:12f0::1/32"}
vr = CreateValidationResults()
uc.Validate(vr)

if !vr.IsEmpty() {
t.Error("limits should be valid")
}

uc.Limits.Src = "192.0.2.0/24,2001:db8:a0b:12f0::1/32"
uc.Limits.Src.Set("192.0.2.0/24, \t2001:db8:a0b:12f0::1/32 , 192.168.1.1/1")
vr = CreateValidationResults()
uc.Validate(vr)

if !vr.IsEmpty() {
t.Error("limits should be valid")
}

uc.Limits.Src = "192.0.2.0/24 ,\t2001:db8:a0b:12f0::1/32 , 192.168.1.1/1"
uc.Limits.Src.Set("192.0.2.0/24,2001:db8:a0b:12f0::1/32,192.168.1.1/1")
vr = CreateValidationResults()
uc.Validate(vr)

if !vr.IsEmpty() {
t.Error("limits should be valid")
}

uc.Limits.Src = "foo"
uc.Limits.Src = CIDRList{"foo"}
vr = CreateValidationResults()
uc.Validate(vr)

if vr.IsEmpty() || len(vr.Issues) != 1 {
t.Error("limits should be invalid")
}

uc.Limits.Src = "192.0.2.0/24,foo"
uc.Limits.Src = CIDRList{"192.0.2.0/24", "foo"}
vr = CreateValidationResults()
uc.Validate(vr)

if vr.IsEmpty() || len(vr.Issues) != 1 {
t.Error("limits should be invalid")
}

uc.Limits.Src = "bloo,foo"
uc.Limits.Src = CIDRList{"bloo", "foo"}
vr = CreateValidationResults()
uc.Validate(vr)

Expand Down

0 comments on commit 292806f

Please sign in to comment.