Skip to content

Commit

Permalink
cmd: Add rules management capabilities to the cli
Browse files Browse the repository at this point in the history
  • Loading branch information
arekkas authored and arekkas committed Nov 18, 2017
1 parent bc2e7c1 commit 289c38a
Show file tree
Hide file tree
Showing 52 changed files with 476 additions and 198 deletions.
10 changes: 10 additions & 0 deletions cmd/helper_messages.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package cmd

import (
"fmt"
"os"
)

var corsMessage = `CORS CONTROLS
==============
- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from.
Expand Down Expand Up @@ -37,3 +42,8 @@ var databaseUrl = `- DATABASE_URL: A URL to a persistent backend. Hydra supports
Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true
Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work.`

func fatalf(msg string, args ...interface{}) {
fmt.Printf(msg+"\n", args...)
os.Exit(1)
}
34 changes: 34 additions & 0 deletions cmd/helper_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cmd

import (
"encoding/json"
"fmt"
"os"

"github.com/ory/oathkeeper/sdk/go/oathkeepersdk/swagger"
)

func checkResponse(response *swagger.APIResponse, err error, expectedStatusCode int) {
must(err, "Could not validate token: %s", err)

if response.StatusCode != expectedStatusCode {
fmt.Printf("Command failed because status code %d was expeceted but code %d was received.", expectedStatusCode, response.StatusCode)
os.Exit(1)
return
}
}

func formatResponse(response interface{}) string {
out, err := json.MarshalIndent(response, "", "\t")
must(err, `Command failed because an error ("%s") occurred while prettifying output.`, err)
return string(out)
}

func must(err error, message string, args ...interface{}) {
if err == nil {
return
}

fmt.Fprintf(os.Stderr, message+"\n", args...)
os.Exit(1)
}
14 changes: 0 additions & 14 deletions cmd/migrate.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
Expand Down
11 changes: 1 addition & 10 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,7 @@ var (
// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
Use: "oathkeeper",
Short: "A brief description of your application",
Long: `A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
// Uncomment the following line if your bare application
// has an action associated with it:
// Run: func(cmd *cobra.Command, args []string) { },
Short: "A cloud native Access and Identity Proxy",
}

// Execute adds all child commands to the root command sets flags appropriately.
Expand Down
72 changes: 72 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cmd

import (
"fmt"
"net/http"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestCommandLineInterface(t *testing.T) {
var osArgs = make([]string, len(os.Args))
os.Setenv("DATABASE_URL", "memory")
os.Setenv("HYDRA_URL", "http://does-not-exist.com/")
os.Setenv("HYDRA_CLIENT_ID", "does-not-exist")
os.Setenv("HYDRA_CLIENT_SECRET", "does-not-exist")
copy(osArgs, os.Args)

for _, c := range []struct {
args []string
wait func() bool
expectErr bool
}{
{
args: []string{"serve", "all"},
wait: func() bool {
res, err := http.Get("http://localhost:4455")
if err != nil {
t.Logf("Network error while polling for server: %s", err)
}
defer res.Body.Close()
return err != nil
},
},
{args: []string{"rules", "--endpoint=http://127.0.0.1:4456/", "import", "../stub/rules.json"}},
{args: []string{"rules", "--endpoint=http://127.0.0.1:4456/", "list"}},
{args: []string{"rules", "--endpoint=http://127.0.0.1:4456/", "get", "test-rule-1"}},
{args: []string{"rules", "--endpoint=http://127.0.0.1:4456/", "get", "test-rule-2"}},
{args: []string{"rules", "--endpoint=http://127.0.0.1:4456/", "delete", "test-rule-1"}},
} {
RootCmd.SetArgs(c.args)

t.Run(fmt.Sprintf("command=%v", c.args), func(t *testing.T) {
if c.wait != nil {
go func() {
assert.Nil(t, RootCmd.Execute())
}()
}

if c.wait != nil {
var count = 0
for c.wait() {
t.Logf("Port is not yet open, retrying attempt #%d...", count)
count++
if count > 5 {
t.FailNow()
}
time.Sleep(time.Second)
}
} else {
err := RootCmd.Execute()
if c.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
}
})
}
}
21 changes: 21 additions & 0 deletions cmd/rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
)

// rulesCmd represents the rules command
var rulesCmd = &cobra.Command{
Use: "rules",
Short: "Commands for managing rules",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(cmd.UsageString())
},
}

func init() {
RootCmd.AddCommand(rulesCmd)
rulesCmd.PersistentFlags().StringP("endpoint", "e", "", "The endpoint URL of ORY Oathkeeper's management API (required)")
}
48 changes: 48 additions & 0 deletions cmd/rules_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package cmd

import (
"fmt"

"net/http"

"github.com/ory/oathkeeper/sdk/go/oathkeepersdk"
"github.com/spf13/cobra"
)

// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
Use: "delete <id>",
Short: "Delete a rule",
Long: `Usage example:
oathkeeper rules --endpoint=http://localhost:4456/ delete rule-1
`,
Run: func(cmd *cobra.Command, args []string) {
endpoint, _ := cmd.Flags().GetString("endpoint")
if endpoint == "" {
fatalf("Please specify the endpoint url using the --endpoint flag, for more information use `oathkeeper help rules`")
} else if len(args) != 1 {
fatalf("Please specify the rule id, for more information use `oathkeeper help rules delete`")
}

client := oathkeepersdk.NewSDK(endpoint)
response, err := client.DeleteRule(args[0])
checkResponse(response, err, http.StatusNoContent)
fmt.Printf("Successfully deleted rule %s\n", args[0])
},
}

func init() {
rulesCmd.AddCommand(deleteCmd)

// Here you will define your flags and configuration settings.

// Cobra supports Persistent Flags which will work for this command
// and all subcommands, e.g.:
// deleteCmd.PersistentFlags().String("foo", "", "A help for foo")

// Cobra supports local flags which will only run when this command
// is called directly, e.g.:
// deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")

}
35 changes: 35 additions & 0 deletions cmd/rules_get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package cmd

import (
"net/http"

"github.com/ory/oathkeeper/sdk/go/oathkeepersdk"
"github.com/spf13/cobra"
)

// getCmd represents the get command
var getCmd = &cobra.Command{
Use: "get <id>",
Short: "Fetch a rule",
Long: `Usage example:
oathkeeper rules --endpoint=http://localhost:4456/ get rule-1
`,
Run: func(cmd *cobra.Command, args []string) {
endpoint, _ := cmd.Flags().GetString("endpoint")
if endpoint == "" {
fatalf("Please specify the endpoint url using the --endpoint flag, for more information use `oathkeeper help rules`")
} else if len(args) != 1 {
fatalf("Please specify the rule id, for more information use `oathkeeper help rules get`")
}

client := oathkeepersdk.NewSDK(endpoint)
rules, response, err := client.GetRule(args[0])
checkResponse(response, err, http.StatusOK)
formatResponse(rules)
},
}

func init() {
rulesCmd.AddCommand(getCmd)
}
57 changes: 57 additions & 0 deletions cmd/rules_import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package cmd

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

"github.com/ory/oathkeeper/sdk/go/oathkeepersdk"
"github.com/ory/oathkeeper/sdk/go/oathkeepersdk/swagger"
"github.com/spf13/cobra"
)

// importCmd represents the import command
var importCmd = &cobra.Command{
Use: "import <file>",
Short: "Import rules from a JSON file",
Long: `The JSON file must be formatted as an array containing one or more rules:
[
{ id: "rule-1", ... },
{ id: "rule-2", ... },
]
Usage example:
oathkeeper rules --endpoint=http://localhost:4456/ import rules.json
`,
Run: func(cmd *cobra.Command, args []string) {
endpoint, _ := cmd.Flags().GetString("endpoint")
if endpoint == "" {
fatalf("Please specify the endpoint url using the --endpoint flag, for more information use `oathkeeper help rules`")
} else if len(args) != 1 {
fatalf("Please specify a JSON file to load the rule definitions from, for more information use `oathkeeper help rules import`")
}

file, err := ioutil.ReadFile(args[0])
must(err, "Reading file %s resulted in error %s", args[0], err)

var rules []swagger.Rule
err = json.Unmarshal(file, &rules)
must(err, "Decoding file contents from JSON resulted in error %s", err)

for _, r := range rules {
fmt.Printf("Importing rule %s...\n", r.Id)
client := oathkeepersdk.NewSDK(endpoint)
out, response, err := client.CreateRule(r)
checkResponse(response, err, http.StatusCreated)
fmt.Printf("Successfully imported rule %s...\n", out.Id)
}
fmt.Printf("Successfully imported all rules from %s", args[0])
},
}

func init() {
rulesCmd.AddCommand(importCmd)
}
33 changes: 33 additions & 0 deletions cmd/rules_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cmd

import (
"net/http"

"github.com/ory/oathkeeper/sdk/go/oathkeepersdk"
"github.com/spf13/cobra"
)

// listCmd represents the list command
var listCmd = &cobra.Command{
Use: "list",
Short: "List all available rules",
Long: `Usage example:
oathkeeper rules --endpoint=http://localhost:4456/ list
`,
Run: func(cmd *cobra.Command, args []string) {
endpoint, _ := cmd.Flags().GetString("endpoint")
if endpoint == "" {
fatalf("Please specify the endpoint url using the --endpoint flag, for more information use `oathkeeper help rules`")
}

client := oathkeepersdk.NewSDK(endpoint)
rules, response, err := client.ListRules()
checkResponse(response, err, http.StatusOK)
formatResponse(rules)
},
}

func init() {
rulesCmd.AddCommand(listCmd)
}
Loading

0 comments on commit 289c38a

Please sign in to comment.