Skip to content

Commit

Permalink
rule: Add migration capabilities (#268)
Browse files Browse the repository at this point in the history
Adds the ability to modify rules with backwards compatibility.

Closes #266
  • Loading branch information
aeneasr authored Sep 26, 2019
1 parent 97d7890 commit bc74e72
Show file tree
Hide file tree
Showing 12 changed files with 169 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ builds:
flags:
- -a
ldflags:
- -s -w -X github.com/ory/oathkeeper/cmd.Version={{.Env.RELEASE_NAME}} -X github.com/ory/oathkeeper/cmd.Commit={{.FullCommit}} -X github.com/ory/oathkeeper/cmd.Date={{.Date}}
- -s -w -X github.com/ory/oathkeeper/x.Version={{.Env.RELEASE_NAME}} -X github.com/ory/oathkeeper/x.Commit={{.FullCommit}} -X github.com/ory/oathkeeper/x.Date={{.Date}}
binary: oathkeeper
env:
- CGO_ENABLED=0
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ install-stable:
git checkout $$OATHKEEPER_LATEST
packr2
GO111MODULE=on go install \
-ldflags "-X github.com/ory/oathkeeper/cmd.Version=$$OATHKEEPER_LATEST -X github.com/ory/oathkeeper/cmd.Date=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/oathkeeper/cmd.Commit=`git rev-parse HEAD`" \
-ldflags "-X github.com/ory/oathkeeper/x.Version=$$OATHKEEPER_LATEST -X github.com/ory/oathkeeper/x.Date=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/oathkeeper/x.Commit=`git rev-parse HEAD`" \
.
packr2 clean
git checkout master
Expand Down
8 changes: 4 additions & 4 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ Previously, this value was only configurable in the global config. Now, it can
be set on a per rule basis as well as globally. The global config will always be
used as a fallback when no access rule specific configuration is set.

For this to work, the ORY Oathkeeper global configuration file (`~/.oathkeeper.yaml`) has changed when it
comes to mutators, authenticaotrs, and authorizers. Instead of defining the
config at the same level as the `enabled` flag, it is now nested in a subkey
"config":
For this to work, the ORY Oathkeeper global configuration file
(`~/.oathkeeper.yaml`) has changed when it comes to mutators, authenticaotrs,
and authorizers. Instead of defining the config at the same level as the
`enabled` flag, it is now nested in a subkey "config":

```
authorizers:
Expand Down
6 changes: 0 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ import (
"github.com/ory/x/logrusx"
)

var (
Version = "master"
Date = "undefined"
Commit = "undefined"
)

var schemas = packr.New("schemas", "../.schemas")

// RootCmd represents the base command when called without any subcommands
Expand Down
3 changes: 2 additions & 1 deletion cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package cmd

import (
"github.com/ory/oathkeeper/cmd/server"
"github.com/ory/oathkeeper/x"

"github.com/ory/x/logrusx"
"github.com/ory/x/viperx"
Expand All @@ -41,7 +42,7 @@ on configuration options, open the configuration documentation:
>> https://www.ory.sh/docs/oathkeeper/configuration <<
`,
Run: server.RunServe(Version, Commit, Date),
Run: server.RunServe(x.Version, x.Commit, x.Date),
}

func init() {
Expand Down
8 changes: 5 additions & 3 deletions cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ import (
"fmt"

"github.com/spf13/cobra"

"github.com/ory/oathkeeper/x"
)

// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display this binary's version, build time and git hash of this build",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Version: %s\n", Version)
fmt.Printf("Git Hash: %s\n", Commit)
fmt.Printf("Build Time: %s\n", Date)
fmt.Printf("Version: %s\n", x.Version)
fmt.Printf("Git Hash: %s\n", x.Commit)
fmt.Printf("Build Time: %s\n", x.Date)
},
}

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf
github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7
github.com/blang/semver v3.5.1+incompatible
github.com/bxcodec/faker v2.0.1+incompatible
github.com/cenkalti/backoff v2.1.1+incompatible
github.com/dgrijalva/jwt-go v3.2.0+incompatible
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7 h1:irR1cO6
github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7/go.mod h1:LWMyo4iOLWXHGdBki7NIht1kHru/0wM179h+d3g8ATM=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA=
Expand Down
31 changes: 31 additions & 0 deletions rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ type Rule struct {
// You will need this ID later on to update or delete the rule.
ID string `json:"id"`

// Version represents the access rule version. Should match one of ORY Oathkeepers release versions. Supported since
// v0.20.0-beta.1+oryOS.14.
Version string `json:"version"`

// Description is a human readable description of this rule.
Description string `json:"description"`

Expand Down Expand Up @@ -110,6 +114,8 @@ type Upstream struct {
URL string `json:"url"`
}

var _ json.Unmarshaler = new(Rule)

func NewRule() *Rule {
return &Rule{
Match: RuleMatch{},
Expand All @@ -118,6 +124,31 @@ func NewRule() *Rule {
}
}

func (r *Rule) UnmarshalJSON(raw []byte) error {
var rr struct {
ID string `json:"id"`
Version string `json:"version"`
Description string `json:"description"`
Match RuleMatch `json:"match"`
Authenticators []RuleHandler `json:"authenticators"`
Authorizer RuleHandler `json:"authorizer"`
Mutators []RuleHandler `json:"mutators"`
Upstream Upstream `json:"upstream"`
}

transformed, err := migrateRuleJSON(raw)
if err != nil {
return err
}

if err := errors.WithStack(json.Unmarshal(transformed, &rr)); err != nil {
return err
}

*r = rr
return nil
}

// GetID returns the rule's ID.
func (r *Rule) GetID() string {
return r.ID
Expand Down
46 changes: 46 additions & 0 deletions rule/rule_migrator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package rule

import (
"strings"

"github.com/blang/semver"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"

"github.com/ory/herodot"
"github.com/ory/x/stringsx"

"github.com/ory/oathkeeper/x"
)

func migrateRuleJSON(raw []byte) ([]byte, error) {
rv := strings.TrimPrefix(
stringsx.Coalesce(
gjson.GetBytes(raw, "version").String(),
x.Version,
x.UnknownVersion,
),
"v",
)

if rv == x.UnknownVersion {
return raw, nil
}

version, err := semver.Make(rv)
if err != nil {
return nil, errors.WithStack(err)
}

raw, err = sjson.SetBytes(raw, "version", strings.Split(x.Version, "+")[0])
if err != nil {
return nil, errors.WithStack(err)
}

if semver.MustParseRange(">=0.19.0-alpha.0")(version) {
return raw, nil
}

return nil, errors.WithStack(herodot.ErrBadRequest.WithReasonf("Unknown access rule version %s, unable to migrate.", version.String()))
}
67 changes: 67 additions & 0 deletions rule/rule_migrator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package rule

import (
"bytes"
"encoding/json"
"fmt"
"testing"

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

"github.com/ory/oathkeeper/x"
)

func TestRuleMigration(t *testing.T) {
for k, tc := range []struct {
d string
in string
out string
expectErr bool
version string
}{
{
d: "should work with v0.19.0-beta.1",
in: `{}`,
out: `{"id":"","version":"v0.19.0-beta.1","description":"","match":{"methods":null,"url":""},"authenticators":null,"authorizer":{"handler":"","config":null},"mutators":null,"upstream":{"preserve_host":false,"strip_path":"","url":""}}`,
version: "v0.19.0-beta.1",
},
{
d: "should work with v0.19.0-beta.1+oryOS.12",
in: `{}`,
out: `{"id":"","version":"v0.19.0-beta.1","description":"","match":{"methods":null,"url":""},"authenticators":null,"authorizer":{"handler":"","config":null},"mutators":null,"upstream":{"preserve_host":false,"strip_path":"","url":""}}`,
version: "v0.19.0-beta.1+oryOS.12",
},
{
d: "should work with v0.19.0-beta.1",
in: `{"version":"v0.19.0-beta.1"}`,
out: `{"id":"","version":"v0.19.0-beta.1","description":"","match":{"methods":null,"url":""},"authenticators":null,"authorizer":{"handler":"","config":null},"mutators":null,"upstream":{"preserve_host":false,"strip_path":"","url":""}}`,
version: "v0.19.0-beta.1",
},
{
d: "should work with 0.19.0-beta.1",
in: `{"version":"0.19.0-beta.1"}`,
out: `{"id":"","version":"v0.19.0-beta.1","description":"","match":{"methods":null,"url":""},"authenticators":null,"authorizer":{"handler":"","config":null},"mutators":null,"upstream":{"preserve_host":false,"strip_path":"","url":""}}`,
version: "v0.19.0-beta.1+oryOS.12",
},
} {
t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
var r Rule

x.Version = tc.version
err := json.NewDecoder(bytes.NewBufferString(tc.in)).Decode(&r)
x.Version = x.UnknownVersion

if tc.expectErr {
require.Error(t, err)
return
}

require.NoError(t, err, "%+v", err)

var out bytes.Buffer
require.NoError(t, json.NewEncoder(&out).Encode(&r))
assert.JSONEq(t, tc.out, out.String(), "%s", out.String())
})
}
}
9 changes: 9 additions & 0 deletions x/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package x

const UnknownVersion = "master"

var (
Version = "master"
Date = "undefined"
Commit = "undefined"
)

0 comments on commit bc74e72

Please sign in to comment.