casbin-server icon indicating copy to clipboard operation
casbin-server copied to clipboard

Request For Comments: Casbin-server V2 Api

Open Leonardo-Ferreira opened this issue 2 years ago • 9 comments

I would like to propose the following gRPC proto definition to be implemented on what I believe to be the V2 of casbin-server

syntax = "proto3";

option java_multiple_files = true;
option java_package = "grpc.CasbinOrg.proto";
option java_outer_classname = "CasbinProto";
option go_package = "./;proto";
option csharp_namespace = "CasbinOrg.Grpc";

package casbin.rpc;

// How this works
// Upon installation, you would get a empty cluster with the root domain 
// representing the cluster itself. It would also contain a single user 
// and a single JWKS config pointing to a local, pre-installed key-pair 
// (which ideally you would change right away, but you can also "disable").
// The base domain would be backed by a local file adapter.
// First the casbin admin would register the adapters for the domains. Then
// he would register the JWKSs. Thirdly he would then register the domains,
// point out which domain is backed by which adapter, being the requests 
// validated against one or more key-pair
service ClusterManagementService{
    rpc GetAdapters (EmptyRequest) returns (stream AdapterDescription) {}
    rpc AddAdapter (AdapterDescription) returns (EmptyReply) {}
    rpc DeleteAdapter (AdapterDescription) returns (EmptyReply) {}
  
    rpc RegisterJWKS (JwksUri) returns (EmptyReply) {}
    rpc GetRegistredJWKSs (FilteredRequest) returns (GetRegistredJWKSsReply) {}
    rpc DeleteRegistredJWKS (JwksUri) returns (EmptyReply){}
  
    rpc RegisterDomain (DomainRegistry) returns (EmptyReply) {}
    rpc GetDomains (FilteredRequest) returns (stream DomainRegistry) {}
    rpc DeleteDomain (DomainRegistry) returns (EmptyReply) {}
}
// The main Casbin service definition.
// The client would instantiate this service, request a enforce compatible
// with its scope and then enforce away
service EnforcementService {
// Retrieves a enforcer based on the name of the domain provided. If security
// is up, the sub identified on the JWT would be enforced as:
// myMicroservice, domainNameInformedOnTheRequest, enforcers, read
// If security is turned off
  rpc GetEnforcer (GetEnforcerRequest) returns (GetEnforcerReply) {}

  //Unary request for enforcement
  rpc Enforce (EnforceRequest) returns (BoolReply) {}
  //Stream based, out of order request for enforcement. Designed for extremely
  //high-throughput scenarios
  rpc Enforce (stream StreamEnforceRequest) returns (stream StreamBoolReply) {}
}
service PolicesManagementService{
  rpc SavePolicy (EmptyRequest) returns (EmptyReply) {}

  //Removed "named" version of the api granting PolicyRequest a "name" field
  rpc AddPolicy (PolicyRequest) returns (BoolReply) {}
  rpc RemovePolicy (PolicyRequest) returns (BoolReply) {}
  rpc RemoveFilteredPolicy (FilteredPolicyRequest) returns (BoolReply) {}
  rpc GetPolicy (FilteredRequest) returns (Array2DReply) {}
  rpc GetFilteredPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
  rpc AddGroupingPolicy (PolicyRequest) returns (BoolReply) {}
  rpc RemoveGroupingPolicy (PolicyRequest) returns (BoolReply) {}
  rpc RemoveFilteredGroupingPolicy (FilteredPolicyRequest) returns (BoolReply) {}
  rpc GetGroupingPolicy (FilteredRequest) returns (Array2DReply) {}
  rpc GetFilteredGroupingPolicy (FilteredPolicyRequest) returns (Array2DReply) {}
}
service UsersAndRolesManagementService{
  rpc GetAllSubjects (FilteredRequest) returns (ArrayReply) {}
  rpc GetAllObjects (FilteredRequest) returns (ArrayReply) {}
  rpc GetAllActions (FilteredRequest) returns (ArrayReply) {}
  rpc GetAllRoles (FilteredRequest) returns (ArrayReply) {}

  rpc HasPolicy (PolicyRequest) returns (BoolReply) {}
  rpc HasGroupingPolicy (PolicyRequest) returns (BoolReply) {}

  rpc GetRolesForUser (GetRolesForUserRequest) returns (ArrayReply) {}
  rpc GetUsersForRole (UserRoleRequest) returns (ArrayReply) {}
  rpc HasRoleForUser (UserRoleRequest) returns (BoolReply) {}
  rpc AddRoleForUser (UserRoleRequest) returns (BoolReply) {}
  rpc DeleteRoleForUser (UserRoleRequest) returns (BoolReply) {}
  rpc DeleteRolesForUser (UserRoleRequest) returns (BoolReply) {}
  rpc DeleteUser (UserRoleRequest) returns (BoolReply) {}
  rpc DeleteRole (UserRoleRequest) returns (EmptyReply) {}

  rpc GetPermissionsForUser (GetPermissionsForUserRequest) returns (Array2DReply) {}
  rpc DeletePermission (PermissionRequest) returns (BoolReply) {}
  rpc AddPermissionForUser (PermissionRequest) returns (BoolReply) {}
  rpc DeletePermissionForUser (PermissionRequest) returns (BoolReply) {}
  rpc DeletePermissionsForUser (PermissionRequest) returns (BoolReply) {}
  rpc HasPermissionForUser (PermissionRequest) returns (BoolReply) {}
}

message DomainRegistry{
    string name = 1;
    AdapterDescription adapter = 2;
    string owner = 3;
    repeated JwksUri jwks_settings = 4;
    int32 created_at = 5;
    int32 last_updated = 6;
    string created_by = 7;
    string last_updated_by = 8;
}

message GetRegistredJWKSsReply{
    repeated RegistredJWKS RegistredJWKSs = 1;
}

message RegistredJWKS{
    JwksUri uri = 1;
    repeated DomainRegistry usedBy = 2;
}

message JwksUri{
    string uri = 1;
}

message AdapterDescription{
    string realm = 1;
    string connectionString = 2;
    bool active = 3;
    int32 createdAt = 4;
    int32 lastUpdated = 5;
    string createdBy = 6;
    string lastUpdatedBy = 7;
}

message GetEnforcerRequest {
  string domainName = 1;
}

message EnforcerHandler {
  int32 handler = 1;
}

message NewAdapterRequest {
  string adapterName = 1;
  string driverName = 2;
  string connectString = 3;
  bool dbSpecified = 4;
}

message NewAdapterReply {
  int32 handler = 1;
}

message EnforceRequest {
  int32 enforcerHandler = 1;
  repeated string params = 2;
}

message StreamEnforceRequest {
    int32 operationId = 1;
    int32 enforcerHandler = 2;
    repeated string params = 3;
}

message StreamBoolReply {
    int32 operationId = 1;
    bool res = 2;
}

message BoolReply {
  bool res = 1;
}

message EmptyRequest {
  int32 handler = 1;
}

message EmptyReply {
}

message PolicyRequest {
  int32 enforcerHandler = 1;
  string pType = 2;
  string name = 3;
  repeated string params = 4;
}

message FilteredRequest {
  int32 enforcerHandler = 1;
  repeated string filters = 2;
  int32 skip = 3;
  int32 take = 4;
}

message ArrayReply {
  repeated string array = 1;
}

message FilteredPolicyRequest {
  int32 enforcerHandler = 1;
  string pType = 2;
  string name = 3;
  int32 fieldIndex = 4;
  repeated string fieldValues = 5;
}

message UserRoleRequest {
  int32 enforcerHandler = 1;
  string user = 2;
  string role = 3;
}

message GetRolesForUserRequest{
  int32 enforcerHandler = 1;
  string user = 2;
  bool implicit_roles_only = 3;
}

message PermissionRequest {
  int32 enforcerHandler = 1;
  string user = 2;
  repeated string permissions = 3;
}

message GetPermissionsForUserRequest {
    int32 enforcerHandler = 1;
    string user = 2;
    repeated string permissions = 3;
    bool implicit_permissions_only = 4;
  }

message Array2DReply {
  message d {
    repeated string d1 = 1;
  }

  repeated d d2 = 1;
}

Most important changes:

  • Operations are group by specific areas, which would simplify SDKs readability. E.G. casbin main object would be consistent of only 2 methods: getenforcer and enforce.
  • You won't be able to request new adapters or enforcers. You ask for enforcers only and the server will determine if you get a new one, or reuse a existing one.
  • operations like "AddNamedPolicy", "AddNamedGroupingPolicy" and "GetImplicitRolesForUser" were removed and the parameter for the "non-named/implicit" operations were increased by those fields

Leonardo-Ferreira avatar Nov 16 '21 21:11 Leonardo-Ferreira

@tangyang9464 @closetool @sagilio

casbin-bot avatar Nov 16 '21 21:11 casbin-bot

@nodece @sagilio @abichinger @Abingcbc plz comment

hsluoyz avatar Nov 17 '21 13:11 hsluoyz

Looks great! We do need a clearer separation of API, and according to #32, it may also be necessary to communicate with the identity provider in some cases. This design solved the two issues. I only have a few questions about the details:

  1. Remote identity providers should be more abstract and pluggable. we are just to be able to obtain user information and verify whether the information is credible, for jwt we can indeed resolve locally, but it is not appropriate to only depend on the jwt, this relationship is more like using casbin-server as a client, just like the relationship between casbin-server as a client, like casdoor-go-sdk and casdoor.
  2. About the service name.
  • I think Casbin and ClusterAdministration are not at the same level, It might be more clear to change Casbin to Enforce.
  • Management may be better than Administration
  • The service name is better to end with Service
  • c-sharp namespace is better to use Casbin.Grpc or Casbin.Proto.
  1. Does proto3 need an optional key? I remember that all fields are optional by default at proto3.

sagilio avatar Nov 17 '21 15:11 sagilio

thank you @sagilio.

as for item 1, this is would be the first stab the evolution/issue: #57 please review the proposition also

as for items 2 ok, ~~will do~~ done

as for item 3, yes its still there: https://developers.google.com/protocol-buffers/docs/proto#optional

Leonardo-Ferreira avatar Nov 17 '21 23:11 Leonardo-Ferreira

@Leonardo-Ferreira proto3 use the same optional behavior in proto2 by default, https://developers.google.com/protocol-buffers/docs/proto3#default

sagilio avatar Nov 18 '21 05:11 sagilio

@sagilio fixed!

Leonardo-Ferreira avatar Nov 18 '21 17:11 Leonardo-Ferreira

@hsluoyz, @sagilio, can we move forward with this?

Leonardo-Ferreira avatar Nov 21 '21 20:11 Leonardo-Ferreira

@Leonardo-Ferreira does this V2 have breaking change with V1? We better don't introduce breaking change.

You won't be able to request new adapters or enforcers. You ask for enforcers only and the server will determine if you get a new one, or reuse a existing one.

Does a user have scenario to need to explicitly get an adapter? Do anyone (or you) use adapter in Casbin lib?

operations like "AddNamedPolicy", "AddNamedGroupingPolicy" and "GetImplicitRolesForUser" were removed and the parameter for the "non-named/implicit" operations were increased by those fields

Why remove those? I think keeping the same API with Casbin lib will have benefit, as the users won't need to learn two different set of APIs.

hsluoyz avatar Nov 22 '21 00:11 hsluoyz

Need more comments @nodece @prathik-kaliyambath @anton-khodak @chmorgan @dautushenka

hsluoyz avatar Nov 22 '21 00:11 hsluoyz

hey everyone... im "unearthing" this topic... my project is being re-prioritize over here and im trying to avoid recreating the wheel... @hsluoyz can you check if your position of "I think its doable, lets get more eyes on this" still stands?

Leonardo-Ferreira avatar Jul 15 '23 17:07 Leonardo-Ferreira

@Leonardo-Ferreira does this V2 have breaking change with V1? We better don't introduce breaking change.

You won't be able to request new adapters or enforcers. You ask for enforcers only and the server will determine if you get a new one, or reuse a existing one.

Does a user have scenario to need to explicitly get an adapter? Do anyone (or you) use adapter in Casbin lib?

operations like "AddNamedPolicy", "AddNamedGroupingPolicy" and "GetImplicitRolesForUser" were removed and the parameter for the "non-named/implicit" operations were increased by those fields

Why remove those? I think keeping the same API with Casbin lib will have benefit, as the users won't need to learn two different set of APIs.

We can keep them backward compatible, but the underlying behavior would change (for the better)

Leonardo-Ferreira avatar Jul 15 '23 17:07 Leonardo-Ferreira

@Leonardo-Ferreira Casbin-Server has been not very active (such as not much development, not many new issues and discussions) these years. There are not many maintainers either.

These years my focus is more on the new project: https://github.com/casdoor/casdoor. Casdoor also exposes the Casbin API: https://casdoor.org/docs/permission/exposed-casbin-apis and should be a complete and better successor of Casbin-Server. Casbin-Server v1 will still be maintained here (like bug fix, small-size and back-compatible feature PRs), but v2 will probably not come.

I hope people who want big features like v2 for this project gradually migrate to Casdoor and contribute there. We would like to help there. If anyone still insists in building large features like v2 on top of this codebase, he/she can just fork it. unfortunately I can't put more efforts on this.

hsluoyz avatar Jul 19 '23 13:07 hsluoyz

@hsluoyz im a bit sad with this news... im reluctant to use Casdoor because being a full blown IAM/SSO tool, my corporate security department will put it through A LOT more scrutiny because of its Authentication features (despite the fact that we won't be using those features)... I feel that Casbin-server is a better fit because of it's focused scope

Perhaps we can "bisect" casdoor into a stand-alone authorization module/microservice?

Leonardo-Ferreira avatar Jul 20 '23 15:07 Leonardo-Ferreira

Closed as stale

hsluoyz avatar Sep 17 '23 17:09 hsluoyz