Repository: nats-io/nsc
Pull request #297 (2020-09-11 01:45:12)

Added support for logon times
Signed-off-by: Matthias Hanel <mh@synadia.com>

```


19:45:30  ~/repos/nsc  master *9 !69 ?30  nsc edit user -n foo --time "16:04:05-17:04:09" --time "18:04:05-19:04:09"
[ OK ] added time range 16:04:05-17:04:09
[ OK ] added time range 18:04:05-19:04:09
[ OK ] generated user creds file `~/.nkeys/creds/test3/SYS/foo.creds`
[ OK ] edited user "foo"
 19:45:55  ~/repos/nsc  master *9 !69 ?30  nsc describe user foo
╭─────────────────────────────────────────────────────────────────────────────────╮
│                                      User                                       │
├──────────────────────┬──────────────────────────────────────────────────────────┤
│ Name                 │ foo                                                      │
│ User ID              │ UCQ4Q4FZNSCQBM3AXOKCZEQ6G6M7IDFMLVGWAWZBW736HHI23MUUI4PB │
│ Issuer ID            │ ABBYA7BW3OCTRL76JFP4BRWIRB2G53XUAHTK5JG4VW34VAB4HOZGFND7 │
│ Issued               │ 2020-09-10 23:45:30 UTC                                  │
│ Expires              │                                                          │
│ Bearer Token         │ No                                                       │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Response Permissions │ Not Set                                                  │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Max Messages         │ Unlimited                                                │
│ Max Msg Payload      │ Unlimited                                                │
│ Network Src          │ Any                                                      │
│ Time                 │ 16:04:05-17:04:09                                        │
│                      │ 18:04:05-19:04:09                                        │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Tags                 │ bar                                                      │
│                      │ foo                                                      │
╰──────────────────────┴──────────────────────────────────────────────────────────╯
 19:46:11  ~/repos/nsc  master *9 !69 ?30  nsc edit user -n foo --rm-time "16:04:05-17:04:09" --rm-time "18:04:05-19:04:09"
[ OK ] generated user creds file `~/.nkeys/creds/test3/SYS/foo.creds`
[ OK ] edited user "foo"
 19:46:19  ~/repos/nsc  master *9 !69 ?30  nsc describe user foo
╭─────────────────────────────────────────────────────────────────────────────────╮
│                                      User                                       │
├──────────────────────┬──────────────────────────────────────────────────────────┤
│ Name                 │ foo                                                      │
│ User ID              │ UCQ4Q4FZNSCQBM3AXOKCZEQ6G6M7IDFMLVGWAWZBW736HHI23MUUI4PB │
│ Issuer ID            │ ABBYA7BW3OCTRL76JFP4BRWIRB2G53XUAHTK5JG4VW34VAB4HOZGFND7 │
│ Issued               │ 2020-09-10 23:46:11 UTC                                  │
│ Expires              │                                                          │
│ Bearer Token         │ No                                                       │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Response Permissions │ Not Set                                                  │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Max Messages         │ Unlimited                                                │
│ Max Msg Payload      │ Unlimited                                                │
│ Network Src          │ Any                                                      │
│ Time                 │ 16:04:05-17:04:09                                        │
│                      │ 18:04:05-19:04:09                                        │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Tags                 │ bar                                                      │
│                      │ foo                                                      │
╰──────────────────────┴──────────────────────────────────────────────────────────╯
 19:46:32  ~/repos/nsc  master *9 !69 ?30  nsc edit user -n foo --rm-time "16:04:05" --rm-time "18:04:05"
[ OK ] removed time range 16:04:05-17:04:09
[ OK ] removed time range 18:04:05-19:04:09
[ OK ] generated user creds file `~/.nkeys/creds/test3/SYS/foo.creds`
[ OK ] edited user "foo"
 19:47:07  ~/repos/nsc  master *9 !69 ?30  nsc describe user foo
╭─────────────────────────────────────────────────────────────────────────────────╮
│                                      User                                       │
├──────────────────────┬──────────────────────────────────────────────────────────┤
│ Name                 │ foo                                                      │
│ User ID              │ UCQ4Q4FZNSCQBM3AXOKCZEQ6G6M7IDFMLVGWAWZBW736HHI23MUUI4PB │
│ Issuer ID            │ ABBYA7BW3OCTRL76JFP4BRWIRB2G53XUAHTK5JG4VW34VAB4HOZGFND7 │
│ Issued               │ 2020-09-10 23:46:32 UTC                                  │
│ Expires              │                                                          │
│ Bearer Token         │ No                                                       │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Response Permissions │ Not Set                                                  │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Max Messages         │ Unlimited                                                │
│ Max Msg Payload      │ Unlimited                                                │
│ Network Src          │ Any                                                      │
│ Time                 │ Any                                                      │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Tags                 │ bar                                                      │
│                      │ foo                                                      │
╰──────────────────────┴──────────────────────────────────────────────────────────╯
 19:47:23  ~/repos/nsc  master *9 !69 ?30  nsc edit user -n foo --time "16:04:05-17:04:09" --time "18:04:05-19:04:09" --rm-time "16:04:05"
[ OK ] added time range 16:04:05-17:04:09
[ OK ] added time range 18:04:05-19:04:09
[ OK ] removed time range 16:04:05-17:04:09
[ OK ] generated user creds file `~/.nkeys/creds/test3/SYS/foo.creds`
[ OK ] edited user "foo"
 19:47:26  ~/repos/nsc  master *9 !69 ?30  nsc describe user foo
╭─────────────────────────────────────────────────────────────────────────────────╮
│                                      User                                       │
├──────────────────────┬──────────────────────────────────────────────────────────┤
│ Name                 │ foo                                                      │
│ User ID              │ UCQ4Q4FZNSCQBM3AXOKCZEQ6G6M7IDFMLVGWAWZBW736HHI23MUUI4PB │
│ Issuer ID            │ ABBYA7BW3OCTRL76JFP4BRWIRB2G53XUAHTK5JG4VW34VAB4HOZGFND7 │
│ Issued               │ 2020-09-10 23:47:23 UTC                                  │
│ Expires              │                                                          │
│ Bearer Token         │ No                                                       │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Response Permissions │ Not Set                                                  │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Max Messages         │ Unlimited                                                │
│ Max Msg Payload      │ Unlimited                                                │
│ Network Src          │ Any                                                      │
│ Time                 │ 18:04:05-19:04:09                                        │
├──────────────────────┼──────────────────────────────────────────────────────────┤
│ Tags                 │ bar                                                      │
│                      │ foo                                                      │
╰──────────────────────┴──────────────────────────────────────────────────────────╯
 19:47:27  ~/repos/nsc  master *9 !69 ?30 

```


Patch:
diff --git a/cmd/edituser.go b/cmd/edituser.go
index a38dc55..04bd0b9 100644
--- a/cmd/edituser.go
+++ b/cmd/edituser.go
@@ -19,6 +19,7 @@ import (
 	"fmt"
 	"sort"
 	"strings"
+	"time"
 
 	"github.com/nats-io/jwt"
 	"github.com/nats-io/nkeys"
@@ -66,6 +67,9 @@ nsc edit user --name <n> --rm-response-perms
 		},
 	}
 
+	cmd.Flags().VarP(&params.times, "time", "", fmt.Sprintf(`add start-end time range of the form "%s-%s" (option can be specified multiple times)`, timeFormat, timeFormat))
+	cmd.Flags().StringSliceVarP(&params.rmTimes, "rm-time", "", nil, fmt.Sprintf(`remove start-end time by start time "%s" (option can be specified multiple times)`, timeFormat))
+
 	cmd.Flags().StringSliceVarP(&params.remove, "rm", "", nil, "remove publish/subscribe and deny permissions - comma separated list or option can be specified multiple times")
 
 	cmd.Flags().StringSliceVarP(&params.allowPubs, "allow-pub", "", nil, "add publish permissions - comma separated list or option can be specified multiple times")
@@ -119,6 +123,8 @@ type EditUserParams struct {
 	remove      []string
 	rmSrc       []string
 	src         []string
+	times       timeSlice
+	rmTimes     []string
 	payload     DataParams
 	bearer      bool
 }
@@ -130,7 +136,7 @@ func (p *EditUserParams) SetDefaults(ctx ActionCtx) error {
 
 	if !InteractiveFlag && ctx.NothingToDo("start", "expiry", "rm", "allow-pub", "allow-sub", "allow-pubsub",
 		"deny-pub", "deny-sub", "deny-pubsub", "tag", "rm-tag", "source-network", "rm-source-network", "payload",
-		"rm-response-perms", "max-responses", "response-ttl", "allow-pub-response", "bearer") {
+		"rm-response-perms", "max-responses", "response-ttl", "allow-pub-response", "bearer", "rm-time", "time") {
 		ctx.CurrentCmd().SilenceUsage = false
 		return fmt.Errorf("specify an edit option")
 	}
@@ -314,6 +320,24 @@ func (p *EditUserParams) Run(ctx ActionCtx) (store.Status, error) {
 	sort.Strings(srcList)
 	p.claim.Src = strings.Join(srcList, ",")
 
+	for _, v := range p.times {
+		r.AddOK("added time range %s-%s", v.Start, v.End)
+		p.claim.Times = append(p.claim.Times, v)
+	}
+	for _, vDel := range p.rmTimes {
+		for i, v := range p.claim.Times {
+			if v.Start == vDel {
+				r.AddOK("removed time range %s-%s", v.Start, v.End)
+				a := p.claim.Times
+				// Remove the element at index i from a.
+				copy(a[i:], a[i+1:])          // Shift a[i+1:] left one index.
+				a[len(a)-1] = jwt.TimeRange{} // Erase last element (write zero value).
+				p.claim.Times = a[:len(a)-1]  // Truncate slice.
+				break
+			}
+		}
+	}
+
 	s, err := p.ResponsePermsParams.Run(p.claim, ctx)
 	if err != nil {
 		return nil, err
@@ -380,3 +404,33 @@ func (p *EditUserParams) Run(ctx ActionCtx) (store.Status, error) {
 	}
 	return r, nil
 }
+
+const timeFormat = "hh:mm:ss"
+const timeLayout = "15:04:05"
+
+type timeSlice []jwt.TimeRange
+
+func (t *timeSlice) Set(val string) error {
+	if tk := strings.Split(val, "-"); len(tk) != 2 {
+		return fmt.Errorf(`require format: "%s-%s" got "%s"`, timeLayout, timeLayout, val)
+	} else if _, err := time.Parse(timeLayout, tk[0]); err != nil {
+		return fmt.Errorf(`require format: "%s-%s" could not parse start time "%s"`, timeLayout, timeLayout, tk[0])
+	} else if _, err := time.Parse(timeLayout, tk[1]); err != nil {
+		return fmt.Errorf(`require format: "%s-%s" could not parse end time "%s"`, timeLayout, timeLayout, tk[1])
+	} else {
+		*t = append(*t, jwt.TimeRange{Start: tk[0], End: tk[1]})
+		return nil
+	}
+}
+
+func (t *timeSlice) String() string {
+	values := make([]string, len(*t))
+	for i, r := range *t {
+		values[i] = fmt.Sprintf("%s-%s", r.Start, r.End)
+	}
+	return "[" + strings.Join(values, ",") + "]"
+}
+
+func (t *timeSlice) Type() string {
+	return "time-ranges"
+}


Test patch:
diff --git a/cmd/describeuser_test.go b/cmd/describeuser_test.go
index e4c2e49..e1f0488 100644
--- a/cmd/describeuser_test.go
+++ b/cmd/describeuser_test.go
@@ -230,3 +230,18 @@ func TestDescribeUser_JsonPath(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, fmt.Sprintf("\"%s\"\n", uc.Subject), out)
 }
+
+func TestDescribeUser_Times(t *testing.T) {
+	ts := NewTestStore(t, "operator")
+	defer ts.Done(t)
+
+	ts.AddAccount(t, "A")
+	ts.AddUser(t, "A", "aa")
+	_, _, err := ExecuteCmd(createEditUserCmd(), "--time", "16:04:05-17:04:09")
+	require.NoError(t, err)
+
+	stdout, _, err := ExecuteCmd(createDescribeUserCmd(), "--account", "A", "--name", "aa")
+	require.NoError(t, err)
+	require.Contains(t, stdout, "16:04:05-17:04:09")
+
+}
diff --git a/cmd/edituser_test.go b/cmd/edituser_test.go
index 17557a0..237caef 100644
--- a/cmd/edituser_test.go
+++ b/cmd/edituser_test.go
@@ -123,28 +123,6 @@ func Test_EditUser_Tag(t *testing.T) {
 
 }
 
-func Test_EditUser_Times(t *testing.T) {
-	ts := NewTestStore(t, "edit server")
-	defer ts.Done(t)
-
-	ts.AddUser(t, "A", "a")
-
-	_, _, err := ExecuteCmd(createEditUserCmd(), "--start", "2018-01-01", "--expiry", "2050-01-01")
-	require.NoError(t, err)
-
-	start, err := ParseExpiry("2018-01-01")
-	require.NoError(t, err)
-
-	expiry, err := ParseExpiry("2050-01-01")
-	require.NoError(t, err)
-
-	cc, err := ts.Store.ReadUserClaim("A", "a")
-	require.NoError(t, err)
-	require.NotNil(t, cc)
-	require.Equal(t, start, cc.NotBefore)
-	require.Equal(t, expiry, cc.Expires)
-}
-
 func Test_EditUser_Pubs(t *testing.T) {
 	ts := NewTestStore(t, "edit user")
 	defer ts.Done(t)
@@ -197,6 +175,33 @@ func Test_EditUser_Src(t *testing.T) {
 	require.ElementsMatch(t, strings.Split(cc.Src, ","), []string{"192.0.1.0/8"})
 }
 
+func Test_EditUser_Times(t *testing.T) {
+	ts := NewTestStore(t, "edit user")
+	defer ts.Done(t)
+
+	ts.AddUser(t, "A", "a")
+
+	_, _, err := ExecuteCmd(createEditUserCmd(), "--time", "16:04:05-17:04:09", "--time", "18:04:05-19:04:09")
+	require.NoError(t, err)
+
+	cc, err := ts.Store.ReadUserClaim("A", "a")
+	require.NoError(t, err)
+	require.NotNil(t, cc)
+
+	require.ElementsMatch(t, cc.Times, []jwt.TimeRange{
+		{Start: "16:04:05", End: "17:04:09"},
+		{Start: "18:04:05", End: "19:04:09"}})
+
+	_, _, err = ExecuteCmd(createEditUserCmd(), "--rm-time", "16:04:05")
+	require.NoError(t, err)
+
+	cc, err = ts.Store.ReadUserClaim("A", "a")
+	require.NoError(t, err)
+	require.NotNil(t, cc)
+	require.ElementsMatch(t, cc.Times, []jwt.TimeRange{
+		{Start: "18:04:05", End: "19:04:09"}})
+}
+
 func Test_EditUserSK(t *testing.T) {
 	ts := NewTestStore(t, "O")
 	t.Log(ts.Dir)
diff --git a/cmd/generateprofile_test.go b/cmd/generateprofile_test.go
index 0b050b4..877bf0c 100644
--- a/cmd/generateprofile_test.go
+++ b/cmd/generateprofile_test.go
@@ -18,11 +18,12 @@ package cmd
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/nats-io/jwt"
 	"net/url"
 	"path"
 	"testing"
 
+	"github.com/nats-io/jwt"
+
 	"github.com/stretchr/testify/require"
 )