-
-
Notifications
You must be signed in to change notification settings - Fork 62
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
Integrate with Glewlwyd #100
Comments
/cc @xcaptain |
thank you @hsluoyz! If you need any feedback please let me know. It is something I find useful myself. The target as I see is to be able to configure both OAuth2 and ACLs from a single back-end (Glewlwyd) and be able to re-use any intersecting functionality: Users This would help bridge the gap between securing protocols like MQTT and HTTP and even event-brokers such as EventStore or Kafka by exposing the Casbin API as a REST API on the Glewlwyd SSO Server but in the meantime partially re-using existing(intersecting) functionality such as user, scope, and resource management etc. |
I'd like to work on this. I still need to get into the weeds of Glewlwyd which might take me a while. So I need @xcaptain's help on this. |
Hello, Glewlwyd's author here! I'm available to help improving casbin and/or Glewlwyd to integrate one with another. Glewlwyd's ACL design is based on the OAuth2 scopes: When a user asks for an access token to Glewlwyd, Glewlwyd checks that the user is allowed to grant those scopes, and asks for new authentication factors if the scopes require it. For now, the security model of a user is like a flat list, for example, a user object could look like this: {
username: "dev",
name: "Dave Lopper",
email: "[email protected]",
enabled: true,
scopes: [ "file:read", "file:write", "mail:read", "profile", "openid" ]
} The user model is flexible enough to add other properties, example: I don't know casbin at all, nor its security model so correct me if I'm wrong.
One solution I see for the integration is to create a user backend module, based on the database or ldap module, or both. Another solution is to add a new plugin to handle the interface with the casbin backend. This plugin would have its REST API and would be called to answer more precise questions: "Is this user allowed to read A and write B?". The main problem I see with this solution is that the client and the resource server must implement an interface to the casbin plugin in addition to the OAuth2/OIDC standard. This plugin would also be a duplicate of what already exists in the casbin ecosystem, therefore the benefit is limited. i.e. it's not more or less complicated to ask a casbin interface if "user is allowed to read A and write B?" or ask glewlwyd's casbin plugin the exact same question. A concern I have is that the plugin would not implement a standard but a dedicated interface. And if the casbin interface changes, the plugin must follow the changes. I personally don't mind, we all make free software, but since I don't intend to use casbin for myself, I hope we can both develop the plugin and maintain it over the month or years of its life. |
@babelouest Your idea that encoding policies to OAuth token is very clever, I once built an OAuth server, I use a database table to store the relation of scope and correspond API, not very convenient and can't be abstracted as a library. The OAuth scope model is very simple, it's just an ACL, for example, if a token has scope |
While thinking about it, I came out with a better idea than updating the existing user backends, instead, I'll add a middleware backend system. These middleware backends would be placed between glewlwyd core and the user backends and would be able to access and update the user objects. Their possibility would be potentially limitless with the user object. For example, in the case of casbin, the middleware beckend would do something like this: // user returned by the user backend
{
username: "dev",
name: "Dave Lopper",
email: "[email protected]",
enabled: true,
scopes: [ "file:read", "file:write", "mail:read", "profile", "openid" ]
}
// user updated by the casbin middleware backend
{
username: "dev",
name: "Dave Lopper",
email: "[email protected]",
enabled: true,
scopes: [ "file:read", "file:write", "mail:read", "profile", "openid" ],
roles: ["admin:it", "admin:legal", "admin:shoes"], // added by the middleware
profile: ["left", "right"] // added by the middleware
} This way will be better and easier to implement and to test, and will allow to use backends and middleware in symbiosis, rather than having an updated backend and having to maintain it in parallel of a similar one. |
@EmperorYP7 plz work on this. |
@hsluoyz sure! Should we create the middleware separately, or include it in glewlwyd's source? Are there any examples of such middleware(s) to which I can refer? Can this be a viable starting point? |
The middleware modules implementation is still a WIP, I'm working on the front-end at the moment, the tests will follow, and the documentation too. But you can start working on a middleware module, based on the mock you mention, that's what it's for. The front-end must have an update too to integrate the casbin parameter, we can make that when we're sure what parameters will be required for it. I will be happy to include the casbin middleware module in Glewlwyd's project, assuming we agree on a license. Feel free to ask questions or challenge the interface if you think it should be changed! A few tips on the functions definitions to help you start: /**
* Must inclulde at least "glewlwyd-common.h"
*/
#include "glewlwyd-common.h"
/**
* function executed once when the module file is loaded
* can be used to initiate global startup
* must return a json_t * object with the module description (see mock return value)
*/
json_t * user_middleware_module_load(struct config_module * config);
/**
* function executed once when the module file is closed
* must return G_OK on success, an error value on error
*/
int user_middleware_module_unload(struct config_module * config);
/**
* function executed when a module instance is initiated
* The variable j_parameters will contain the instance parameters
* The variable *cls, if used, must be set by the instance and will be passed as parameter for all other module functions,
* can be used to keep a structure with the connection to a service or anything useful to the module
* must return a json_t * object of the following format:
* {result: G_OK} on success
* {result: G_ERROR*, error: ["error list in a JSON array of strings"]} on error
*/
json_t * user_middleware_module_init(struct config_module * config, json_t * j_parameters, void ** cls);
/**
* function executed when a module instance is closed
* the resources contained in cls must be released here
* must return G_OK on success, an error value on error
*/
int user_middleware_module_close(struct config_module * config, void * cls);
/**
* function executed on a user_get_iist
* this function will update the j_user_list content accordingly to its requirements, or leave it as is if needed
* must return G_OK on success, an error value on error
* The whole user_get_list result will be cancelled if an error is returned, so an error value shouldn't be returned if one user has error for example, better off removing this user of the list
*/
int user_middleware_module_get_list(struct config_module * config, json_t * j_user_list, void * cls);
/**
* function executed on a get_user
* this function will update the j_user content accordingly to its requirements, or leave it as is if needed
* must return G_OK on success, an error value on error
*/
int user_middleware_module_get(struct config_module * config, const char * username, json_t * j_user, void * cls);
/**
* function executed on a get_user_profile, the user_profile is the attributes the user has access to
* this function will update the j_user content accordingly to its requirements, or leave it as is if needed
* must return G_OK on success, an error value on error
*/
int user_middleware_module_get_profile(struct config_module * config, const char * username, json_t * j_user, void * cls);
/**
* function executed before a add_user, set_user or set_user_profile
* this function will update the j_user content accordingly to its requirements, or leave it as is if needed
* must return G_OK on success, an error value on error
*/
int user_middleware_module_update(struct config_module * config, const char * username, json_t * j_user, void * cls);
/**
* function executed before a delete_user
* this function will update the j_user content accordingly to its requirements, or leave it as is if needed
* must return G_OK on success, an error value on error
*/
int user_middleware_module_delete(struct config_module * config, const char * username, json_t * j_user, void * cls); |
Thank you @babelouest. 😄
@hsluoyz I think we should be creating a middleware within glewlwyd's source. |
You can also reach me on IRC, libera network, channel #glewlwyd |
@babelouest @EmperorYP7 thanks for the update! The middleware design looks great :) We usually use Apache 2.0 license in Casbin main repos. But I think it's OK to follow the https://github.com/babelouest/glewlwyd repo's license (which is GPL 3.0) if we put the middleware code directly into it. |
@hsluoyz , no problem for the license. To explain my point of view, I'm open minded with the licences third parties want to use with the modules, that's why the mock modules and the required files to include use MIT license, while the rest of glewlwyd is using GPL3. Concerning my glewlwyd repository, I'll include only Bottom line, I'm ok with a GPL3 license for the casbin module, but I'm not closed to other compatible license in general ;) |
@EmperorYP7 , the middleware modules management is now ready in the master branch. |
That's great! I am on it. Thanks for the update. 😄 |
Quick update: casbin-cpp has not yet been configured to be called within C. This would require developing C wrappers for casbin's intrinsic functions and classes. I'll be working on this next week as well along with benchmarking. |
You may not have to wrap casbin functions: I've written a module in a similar context by adding Something like that: extern "C" json_t * user_middleware_module_init(struct config_module * config, json_t * j_parameters, void ** cls) {
// Do magic here
} The Makefile will use |
Thank you so much. Saved me a lot of time! 😄 @babelouest |
My pleasure |
See: babelouest/glewlwyd#184 (comment)
We should provide help and support including development efforts to help Glewlwyd integrate with Casbin. We need to communicate with them, know our customer's need and help them with the code.
I suggest putting this in a high priority (than the issues proposed by our own people) because we have been "working behind closed doors" for long (we raise issues by ourselves, then solve them) and this can be the first time for us to show Casbin-Cpp is also useful for others.
The text was updated successfully, but these errors were encountered: