Skip to content

Commit

Permalink
Add rules (#171)
Browse files Browse the repository at this point in the history
* step 1: restore from our earlier iteration

* Very rough first pass on create

* Better name

* Wrap up editor prompt / rules create flow

* Rules: CRUD is done

* Cleanup

* Lint

* Finalize rules

Pick 5 templates to start.

* Tweak flags so we don't repeat ourselves too much

* Add docs for flags.Required()

* Switch to using new ansi.Waiting for most cases

* Ask name after fetching rule

* Subtle fix: update && not required should not prompt

* Cleanup

* Add a note for now that we're going to do something for next time

* Remove `Required()` use

* Amend to have AlwaysPrompt logic
  • Loading branch information
cyx authored Mar 20, 2021
1 parent 96b6be4 commit a1ba34b
Show file tree
Hide file tree
Showing 17 changed files with 720 additions and 18 deletions.
1 change: 1 addition & 0 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var requiredScopes = []string{
"offline_access", // <-- to get a refresh token.
"create:clients", "delete:clients", "read:clients", "update:clients",
"create:resource_servers", "delete:resource_servers", "read:resource_servers", "update:resource_servers",
"create:rules", "delete:rules", "read:rules", "update:rules",
"read:client_keys", "read:logs",
}

Expand Down
1 change: 1 addition & 0 deletions internal/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ func TestRequiredScopes(t *testing.T) {
crudResources := []string{
"clients",
"resource_servers",
"rules",
}
crudPrefixes := []string{"create:", "delete:", "read:", "update:"}

Expand Down
2 changes: 2 additions & 0 deletions internal/auth0/auth0.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type API struct {
Client ClientAPI
Log LogAPI
ResourceServer ResourceServerAPI
Rule RuleAPI
}

func NewAPI(m *management.Management) *API {
Expand All @@ -26,6 +27,7 @@ func NewAPI(m *management.Management) *API {
Client: m.Client,
Log: m.Log,
ResourceServer: m.ResourceServer,
Rule: m.Rule,
}
}

Expand Down
25 changes: 25 additions & 0 deletions internal/auth0/rule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:generate mockgen -source=rule.go -destination=rule_mock.go -package=auth0

package auth0

import "gopkg.in/auth0.v5/management"

type RuleAPI interface {
// Create a new rule.
//
// Note: Changing a rule's stage of execution from the default `login_success`
// can change the rule's function signature to have user omitted.
Create(r *management.Rule, opts ...management.RequestOption) error

// Retrieve rule details. Accepts a list of fields to include or exclude in the result.
Read(id string, opts ...management.RequestOption) (r *management.Rule, err error)

// Update an existing rule.
Update(id string, r *management.Rule, opts ...management.RequestOption) error

// Delete a rule.
Delete(id string, opts ...management.RequestOption) error

// List all rules.
List(opts ...management.RequestOption) (r *management.RuleList, err error)
}
11 changes: 6 additions & 5 deletions internal/cli/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ var (
IsRequired: false,
}
appCallbacks = Flag{
Name: "Callback URLs",
LongForm: "callbacks",
ShortForm: "c",
Help: "After the user authenticates we will only call back to any of these URLs. You can specify multiple valid URLs by comma-separating them (typically to handle different environments like QA or testing). Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native apps, all callbacks should use protocol https://.",
IsRequired: false,
Name: "Callback URLs",
LongForm: "callbacks",
ShortForm: "c",
Help: "After the user authenticates we will only call back to any of these URLs. You can specify multiple valid URLs by comma-separating them (typically to handle different environments like QA or testing). Make sure to specify the protocol (https://) otherwise the callback may fail in some cases. With the exception of custom URI schemes for native apps, all callbacks should use protocol https://.",
IsRequired: false,
AlwaysPrompt: true,
}
appOrigins = Flag{
Name: "Allowed Origin URLs",
Expand Down
8 changes: 8 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,11 @@ func prepareInteractivity(cmd *cobra.Command) {
})
}
}

func flagOptionsFromMapping(mapping map[string]string) []string {
result := make([]string, 0, len(mapping))
for k := range mapping {
result = append(result, k)
}
return result
}
8 changes: 8 additions & 0 deletions internal/cli/data/rule-template-add-email-to-access-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function addEmailToAccessToken(user, context, callback) {
// This rule adds the authenticated user's email address to the access token.

var namespace = 'https://example.com/';

context.accessToken[namespace + 'email'] = user.email;
return callback(null, user, context);
}
12 changes: 12 additions & 0 deletions internal/cli/data/rule-template-check-last-password-reset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function checkLastPasswordReset(user, context, callback) {
function daydiff(first, second) {
return (second - first) / (1000 * 60 * 60 * 24);
}

const last_password_change = user.last_password_reset || user.created_at;

if (daydiff(new Date(last_password_change), new Date()) > 30) {
return callback(new UnauthorizedError('please change your password'));
}
callback(null, user, context);
}
3 changes: 3 additions & 0 deletions internal/cli/data/rule-template-empty-rule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function(user, context, cb) {
cb(null, user, context);
}
12 changes: 12 additions & 0 deletions internal/cli/data/rule-template-ip-address-allow-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function ipAddressAllowList(user, context, callback) {
const allowlist = ['1.2.3.4', '2.3.4.5']; // authorized IPs
const userHasAccess = allowlist.some(function (ip) {
return context.request.ip === ip;
});

if (!userHasAccess) {
return callback(new Error('Access denied from this IP address.'));
}

return callback(null, user, context);
}
14 changes: 14 additions & 0 deletions internal/cli/data/rule-template-ip-address-deny-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
function ipAddressDenylist(user, context, callback) {
const denylist = ['1.2.3.4', '2.3.4.5']; // unauthorized IPs
const notAuthorized = denylist.some(function (ip) {
return context.request.ip === ip;
});

if (notAuthorized) {
return callback(
new UnauthorizedError('Access denied from this IP address.')
);
}

return callback(null, user, context);
}
31 changes: 18 additions & 13 deletions internal/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ import (
)

type Flag struct {
Name string
LongForm string
ShortForm string
Help string
IsRequired bool
Name string
LongForm string
ShortForm string
Help string
IsRequired bool
AlwaysPrompt bool
}

func (f Flag) GetName() string {
Expand Down Expand Up @@ -94,14 +95,6 @@ func selectFlag(cmd *cobra.Command, f *Flag, value interface{}, options []string
return nil
}

func shouldAsk(cmd *cobra.Command, f *Flag, isUpdate bool) bool {
if isUpdate {
return shouldPromptWhenFlagless(cmd, f.LongForm)
}

return shouldPrompt(cmd, f.LongForm)
}

func registerString(cmd *cobra.Command, f *Flag, value *string, defaultValue string, isUpdate bool) {
cmd.Flags().StringVarP(value, f.LongForm, f.ShortForm, defaultValue, f.Help)

Expand Down Expand Up @@ -134,6 +127,18 @@ func registerBool(cmd *cobra.Command, f *Flag, value *bool, defaultValue bool, i
}
}

func shouldAsk(cmd *cobra.Command, f *Flag, isUpdate bool) bool {
if isUpdate {
if !f.AlwaysPrompt {
return false
}

return shouldPromptWhenFlagless(cmd, f.LongForm)
}

return shouldPrompt(cmd, f.LongForm)
}

func markFlagRequired(cmd *cobra.Command, f *Flag, isUpdate bool) error {
if f.IsRequired && !isUpdate {
return cmd.MarkFlagRequired(f.LongForm)
Expand Down
1 change: 1 addition & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func Execute() {
rootCmd.AddCommand(loginCmd(cli))
rootCmd.AddCommand(tenantsCmd(cli))
rootCmd.AddCommand(appsCmd(cli))
rootCmd.AddCommand(rulesCmd(cli))
rootCmd.AddCommand(quickstartsCmd(cli))
rootCmd.AddCommand(apisCmd(cli))
rootCmd.AddCommand(testCmd(cli))
Expand Down
Loading

0 comments on commit a1ba34b

Please sign in to comment.