ory / ladon

A SDK for access control policies: authorization for the microservice and IoT age. Inspired by AWS IAM policies. Written for Go.

Home Page:https://www.ory.sh/?utm_source=github&utm_medium=banner&utm_campaign=ladon

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

YAML support for policy Conditions

leplatrem opened this issue · comments

I'm running into several issues when un/marshaling YAML (with "gopkg.in/yaml.v2")

For example, the conditions don't get marshaled at all, and unmarshaling fails with an error like value of type map[interface {}]interface {} is not assignable to type ladon.Condition

I'm a true beginner in Go, but if there's someone to guide me through, I'd be delighted to contribute the necessary changes — unless you consider it out of scope, in which case, we can close this issue :)

Note: It works fine as JSON ;)


package main

import (
  "github.com/ory/ladon"
  "gopkg.in/yaml.v2"
)

func main() {
  var out = []*ladon.DefaultPolicy{
    {
      ID:          "1",
      Description: "description",
      Subjects:    []string{"user"},
      Effect:      ladon.AllowAccess,
      Resources:   []string{"articles:<[0-9]+>"},
      Actions:     []string{"create", "update"},
      Conditions:  ladon.Conditions{
        "owner": &ladon.EqualsSubjectCondition{},
      },
    },
    {
      Effect:     ladon.DenyAccess,
      Conditions: make(ladon.Conditions),
    },
  }
  yamlData, err := yaml.Marshal(out)
  if err != nil {
    panic(err)
  }
  println(string(yamlData))

  var asYAML []*ladon.DefaultPolicy
  if err := yaml.Unmarshal(yamlData, &asYAML); err != nil {
    panic(err)
  }
}

See condition.go if you can implement UnmarshalYAML and MarshalYAML as per the json versions the condition fields can be populated, currently the yaml parser has no way of knowing how to serialize a Condition struct.

Yup, exactly - nice sum up @thetooth

Closing here, but feel free to ask if you've got further questions.

Thanks for your help!

I managed to have something working by adding UnmarshalYAML() methods in condition.go and policy.go. mozilla/doorman@f69d085
It took me some time to figure out how to avoid introducing a dependency to the yaml package — using reflect.

Would it make sense if I contribute those changes? (or an improved version of course)

Thanks again :)

Very cool that you got it working! If I can give you some feedback, I would always try to not use reflection at all in Go - sometimes it's tempting when coming from other languages but it's a noGo (pun intended) and absolute edge case. It's not necessary in the simple logic that we're dealing with here :)

Having said that, I'm 50/50 on having YAML support here. On the one hand, I get that people use and like it - on the other hand I think it's an extremely complicated / ambigoous spec with many edge cases and 84 written pages (JSON is only one page). I've wasted so much of my time getting YAML to work for certain things. I'm not sure if we should use this spec to define very important security features.

However, I don't think it will hurt much to have MarshalYAML and UnmarshalYAML implemented on Condition so I'm open to restrain from my reservations.

What's your primary use case here?

I understand your concerns about opening that door. If there is a risk of introducing more issues or misuse, I agree it's not worth it.

However, I don't think it will hurt much to have MarshalYAML and UnmarshalYAML

That's the part I have here mozilla/doorman@f69d085

I used reflections because I wanted to avoid adding a new dependency to gopkg.in/yaml.v2. But if you think we can do better (and/or agree to add the dependency), then I would be glad to contribute it!

What's your primary use case here?

We find YAML a lot easier to read/write (manually) than JSON. And our use-case is to let some admins write a YAML file with the policies definitions (and stored e.g. on a Github private repo).

Currently, I have a naive approach: I have an intermediary step that transforms the YAML into JSON before deserializing:
https://github.com/leplatrem/iam/blob/501ad2465ccaad08cffef8fa65f1a55b905eee02/warden/warden.go#L74-L88

I was thinking that maybe I'll want to have some nice validation features for the policies.yaml file, and will probably have to unmarshall the file to interface{} for that...

Conclusion: maybe it's out of scope :)

We find YAML a lot easier to read/write (manually) than JSON. And our use-case is to let some admins write a YAML file with the policies definitions (and stored e.g. on a Github private repo).

That makes sense as well, YAML is after all JSON compliant so this should work totally fine.

I used reflections because I wanted to avoid adding a new dependency to gopkg.in/yaml.v2. But if you think we can do better (and/or agree to add the dependency), then I would be glad to contribute it!

If it was to be accepted, I would be fine with adding this dependency.

Conclusion: maybe it's out of scope :)

I agree, seeing that you found a very nice workaround I think it's fair to say that that should be the way. Firstly we don't introduce new dependencies and a (at times) tricky spec, and secondly it's easy to transpile YAML to JSON.

Cheers! :)