Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SpiceWeaver goals? #18

Closed
tanczosm opened this issue Apr 24, 2024 · 8 comments
Closed

SpiceWeaver goals? #18

tanczosm opened this issue Apr 24, 2024 · 8 comments
Labels
question Further information is requested

Comments

@tanczosm
Copy link

Greetings! I'm working on a C# client for SpiceDb and I sorely would love to automate the process of making relationships and permissions strongly-typed and happened upon your repo. Hand-writing everything requires constant referencing of the schema and I'd rather not do that if possible.

My repo is at https://github.com/JalexSocial/SpiceDb

I was wondering what your project goals were if you would be willing to share.

@LykaiosNZ
Copy link
Owner

Howdy! I'm already familiar with your repo and using it at work - have even raised a couple of issues under my work account (thanks for addressing those btw) 😀

In terms of goals, the first one was to solve the problem of having to refer to the schema itself for definition/relation/permission names and referencing these directly or manually creating constants for them. So at present the source generator just generates these constants for you:

namespace SpiceWeaver
{
    public static class Schema
    {
        public static class Definitions
        {
            public static class User
            {
                public const string Name = "user";
            }

            public static class Document
            {
                public const string Name = "document";
                public static class Relations
                {
                    public const string Viewer = "viewer";
                    public const string Editor = "editor";
                }

                public static class Permissions
                {
                    public const string View = "view";
                    public const string Edit = "edit";
                }
            }
        }
    }
}

Ideally, it would evolve into a strongly-typed client that wraps a lower-level client like yours, so consumers would not need to work directly with ResourceReference etc. I haven't given too much thought as to exactly how this would look just yet.

If you think this is something that could live under the SpiceDb.net project, I'd be more than happy to contribute.

One thing to note is that originally I was writing a schema parser myself, but it was using Pidgin which is not compatible with .NET Standard (and therefore Source Generators), so I scrapped that and am now using spice2json instead, which uses the SpiceDB DSL parser to output json. This does mean there is a dependency on this external binary at present, however.

I'm unsure if the Source Generator route is 100% necessary versus something like a simple cli tool that spits out .cs files, but I figured it may be worthwhile for people able to reference and work with the schema file directly. It's a bit of a catch-22 though, given its limitation of targeting .NET Standard.

@tanczosm
Copy link
Author

tanczosm commented Apr 25, 2024

Ah that's how your named looked familiar. I didn't put the two together. Since the recent major refactor you can now supply a ChannelBase directly if so desired for the grpc channel. Try out 1.5.1 if you haven't yet. Relationships can also include caveats now when you create them. I also reorganized a lot of code to clean things up a bit and started work on a documentation project.

If we could unite the two I think that would be great and since you are using it for work it could give you an opportunity to shape the library as well. Honestly it feels like a bit of a lonely island working on the client because it's tough to tell how much SpiceDb is even adopted by the .net community. TBH I saw your comment on the spice2json library so it appears it isn't just limited to .net. I made the .net client out of necessity originally though because I wanted to use it from c# first of all, there wasn't a lot to choose from client-wise, and I wanted to create a wrapper for possibly more than SpiceDb (which is why some of the types don't match exactly in the client) but I've since mostly steered exclusively towards SpiceDb. I do want to be able to provide better quality of life c# code for developers though who want to work with the database though which means if I can create a useful layer on top of the grpc client that could be useful.

One of the aspects I found off-putting with libraries like the Java client is how much code it takes to just check a permission.

For your project, what if you still used spice2json but eliminated the executable dependency (sort of) by just requiring that schemas be run through spice2json first and named with .zedj (or similar) before adding to the project? Then have the source generator just transform that specific file? That way you can just check in the transformed schema directly into your source. It requires an extra step but the ability to use it doesn't require much setup work (especially on remote build servers).

Here is an ANTLR grammar I made. It is missing types for caveats but is a start. The grammar can be used as a basis to build other grammars. Right now I think the spice2json route is the best approach rather than trying to parse it directly.

grammar SpiceDb;

// Parser Rules

schema: (definition | caveat | comment)*;
definition: 'definition' ID '{' definitionBody '}';
definitionBody: (relation | permission | comment)*;
relation: 'relation' ID ':' subject ( '|' subject )*;
permission: 'permission' ID '=' expression;
caveat: 'caveat' ID '(' parameters ')' '{' expression '}';
expression: expression ('+' | '&' | '-' | '->') expression
          | ID
          | '(' expression ')';

parameters: parameter (',' parameter)*;
parameter: ID type;

type: 'int' | 'string' | ID; 

subject: typeReference (subRelation | wildcard)?;
typeReference: ID;
subRelation: '#' ID;
wildcard: ':*';

comment: DOC_COMMENT | LINE_COMMENT | BLOCK_COMMENT;

// Lexer Rules
ID: [a-zA-Z_][a-zA-Z_0-9]*;
DOC_COMMENT: '/**' .*? '*/' -> channel(HIDDEN);
LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN);
BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
WS: [ \t\r\n]+ -> channel(HIDDEN);

Let me know your thoughts.

@tanczosm
Copy link
Author

tanczosm commented Apr 25, 2024

Does Spice2Json pick up on caveats as well? A caveat has a name and context, but the context keys (user_ip and allowed_range) are also well-defined.

ex.

caveat has_valid_ip(user_ip ipaddress, allowed_range string) {
  user_ip.in_cidr(allowed_range)
}

For simplicity sake you could create a class called Caveats that contains all the caveats. If the caveats themselves were classes that inherited SpiceDb.net Caveat that would make working with them pretty simple.

new Caveats.HasValidIp { AllowedRange = "10.20.30.0" }

That would be something like:

public class Caveats {
   public class HasValidIp : Caveat
   {
       public HasValidIp () { this.Name = "has_valid_ip"; }
       public IpAddress? UserIp { 
          get { 
              return this.Context.ContainsKey("user_ip") ? this.Context["user_ip"].ToIpAddress() : null
          }
          set {
              this.Context["user_ip"] = value;
          }
        }
       public string AllowedRange {
           get {
             return this.Context.ContainsKey("allowed_range") ? this.Context["allowed_range"] : null;
           }
           set {
               this.Context["allowed_range"] = value;
           }
       }
   }
}

Note that allowed_range would be supplied when creating the relationship caveat and user_ip could be supplied during permission checks.

@LykaiosNZ
Copy link
Owner

LykaiosNZ commented Apr 25, 2024

For your project, what if you still used spice2json but eliminated the executable dependency (sort of) by just requiring that schemas be run through spice2json first and named with .zedj (or similar) before adding to the project?

I've already taken this into account with an option on the additional file, was just missing documentation to explain

Here is an ANTLR grammar I made. It is missing types for caveats but is a start. The grammar can be used as a basis to build other grammars. Right now I think the spice2json route is the best approach rather than trying to parse it directly.

There's also a tree-sitter grammar written by the co-founder of AuthZed but I found tree-sitter hard to work with.

@LykaiosNZ
Copy link
Owner

An issue was just raised in the SpiceDB repo about having an official .NET client: authzed/spicedb#1877

Be interesting to see what the response to this is.

@tanczosm
Copy link
Author

TBH it's pretty easy to just generate a c# client off the protobuf definitions but I could definitely see how it's easier to have faith in a company if the client is supported by that company. You just have to deal with all the custom google grpc data structures.

I wouldn't have bothered making what I did if there was something official.

@tanczosm
Copy link
Author

tanczosm commented Apr 26, 2024

I'm playing around with the source generator and it's working well. I created a new Relationship object as follows:

using Arch = Archimedes.Schema.Definitions;

new Relationship($"{Arch.Document.Name}:abc123", Arch.Document.Relations.Viewer, $"{Arch.User.Name}:efg456");

Using the definition with an id is extremely common.. maybe add an additional "WithId" method to each definition as a helper? Something generated like this:

public static class User
{
    public const string Name = "user";
    public static string WithId (string id) => $"user:{id}";
}

Then code to use it is:

using Arch = Archimedes.Schema.Definitions;

new Relationship(Arch.Document.WithId("abc123"), Arch.Platform.Relations.Viewer, Arch.User.WithId("efg456"));

Thoughts?

@LykaiosNZ
Copy link
Owner

I like it, if you throw that in a new issue I can take a look tomorrow - should be pretty trivial

@LykaiosNZ LykaiosNZ added the question Further information is requested label Apr 26, 2024
Repository owner locked and limited conversation to collaborators Apr 26, 2024
@LykaiosNZ LykaiosNZ converted this issue into discussion #26 Apr 26, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants