Git Product home page Git Product logo

casbin-rs's Introduction

casbin-rs

GitHub last commit Crates.io crates.io Docs CI Codecov Discord forum

๐Ÿ’– Looking for an open-source identity and access management solution like Okta, Auth0, Keycloak ? Learn more about: Casdoor

casdoor

News: still worry about how to write the correct Casbin policy? Casbin online editor is coming to help! Try it at: https://casbin.org/editor/

casbin Logo

Casbin-RS is a powerful and efficient open-source access control library for Rust projects. It provides support for enforcing authorization based on various access control models.

All the languages supported by Casbin:

golang java nodejs php
Casbin jCasbin node-Casbin PHP-Casbin
production-ready production-ready production-ready production-ready
python dotnet c++ rust
PyCasbin Casbin.NET Casbin-CPP Casbin-RS
production-ready production-ready production-ready production-ready

Installation

Add this package to Cargo.toml of your project. (Check https://crates.io/crates/casbin for right version)

[dependencies]
casbin = { version = "2.3.0", default-features = false, features = ["runtime-async-std", "logging", "incremental"] }
tokio = { version = "1.10.0", features = ["fs", "io-util"] }

Warning: tokio v1.0 or later is supported from casbin v2.0.6, we recommend that you upgrade the relevant components to ensure that they work properly. The last version that supports tokio v0.2 is casbin v2.0.5 , you can choose according to your needs.

Get started

  1. New a Casbin enforcer with a model file and a policy file:
use casbin::prelude::*;

#[tokio::main]
async fn main() -> Result<()> {
    let mut e = Enforcer::new("examples/rbac_with_domains_model.conf", "examples/rbac_with_domains_policy.csv").await?;
    e.enable_log(true);

    e.enforce(("alice", "domain1", "data1", "read"))?;
    Ok(())
}
  1. Add an enforcement hook into your code right before the access happens:

    let sub = "alice"; // the user that wants to access a resource.
    let obj = "data1"; // the resource that is going to be accessed.
    let act = "read"; // the operation that the user performs on the resource.
    
    if let Ok(authorized) = e.enforce((sub, obj, act)) {
        if authorized {
            // permit alice to read data1
        } else {
            // deny the request
        }
    } else {
        // error occurs
    }

๐Ÿ’ก Please note that the Enforcer instance is not thread-safe, so in order to use it in a environment where multiple threads might access it, you have to protect it using an RwLock like so: let e = Arc::new(RwLock::new(e));.

Table of contents

Supported models

  1. ACL (Access Control List)
  2. ACL with superuser
  3. ACL without users: especially useful for systems that don't have authentication or user log-ins.
  4. ACL without resources: some scenarios may target for a type of resources instead of an individual resource by using permissions like write-article, read-log. It doesn't control the access to a specific article or log.
  5. RBAC (Role-Based Access Control)
  6. RBAC with resource roles: both users and resources can have roles (or groups) at the same time.
  7. RBAC with domains/tenants: users can have different role sets for different domains/tenants.
  8. ABAC (Attribute-Based Access Control): syntax sugar like resource.Owner can be used to get the attribute for a resource.
  9. RESTful: supports paths like /res/*, /res/:id and HTTP methods like GET, POST, PUT, DELETE.
  10. Deny-override: both allow and deny authorizations are supported, deny overrides the allow.
  11. Priority: the policy rules can be prioritized like firewall rules.

How it works?

In casbin-rs, an access control model is abstracted into a CONF file based on the PERM metamodel (Policy, Effect, Request, Matchers). So switching or upgrading the authorization mechanism for a project is just as simple as modifying a configuration. You can customize your own access control model by combining the available models. For example, you can get RBAC roles and ABAC attributes together inside one model and share one set of policy rules.

The most basic and simplest model in casbin-rs is ACL. ACL's model CONF is:

# Request definition
[request_definition]
r = sub, obj, act

# Policy definition
[policy_definition]
p = sub, obj, act

# Policy effect
[policy_effect]
e = some(where (p.eft == allow))

# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

An example policy for ACL model is like:

p, alice, data1, read
p, bob, data2, write

It means:

  • alice can read data1
  • bob can write data2

Features

What casbin-rs does:

  1. enforce the policy in the classic {subject, object, action} form or a customized form as you defined, both allow and deny authorizations are supported.
  2. handle the storage of the access control model and its policy.
  3. manage the role-user mappings and role-role mappings (aka role hierarchy in RBAC).
  4. support built-in superuser like root or administrator. A superuser can do anything without explict permissions.
  5. multiple built-in operators to support the rule matching. For example, keyMatch can map a resource key /foo/bar to the pattern /foo*.

What casbin-rs does NOT do:

  1. authentication (aka verify username and password when a user logs in)
  2. manage the list of users or roles. I believe it's more convenient for the project itself to manage these entities. Users usually have their passwords, and casbin-rs is not designed as a password container. However, casbin-rs stores the user-role mapping for the RBAC scenario.

Documentation

https://casbin.org/docs/overview

Online editor

You can also use the online editor (http://casbin.org/editor/) to write your casbin-rs model and policy in your web browser. It provides functionality such as syntax highlighting and code completion, just like an IDE for a programming language.

Tutorials

https://casbin.org/docs/tutorials

Policy management

casbin-rs provides two sets of APIs to manage permissions:

  • Management API: the primitive API that provides full support for casbin-rs policy management. See here for examples.
  • RBAC API: a more friendly API for RBAC. This API is a subset of Management API. The RBAC users could use this API to simplify the code. See here for examples.

We also provide a web-based UI for model management and policy management:

model editor

policy editor

Policy persistence

Role manager

https://casbin.org/docs/role-managers

Examples

Model Model file Policy file
ACL basic_model.conf basic_policy.csv
ACL with superuser basic_model_with_root.conf basic_policy.csv
ACL without users basic_model_without_users.conf basic_policy_without_users.csv
ACL without resources basic_model_without_resources.conf basic_policy_without_resources.csv
RBAC rbac_model.conf rbac_policy.csv
RBAC with resource roles rbac_model_with_resource_roles.conf rbac_policy_with_resource_roles.csv
RBAC with domains/tenants rbac_model_with_domains.conf rbac_policy_with_domains.csv
ABAC abac_model.conf N/A
RESTful keymatch_model.conf keymatch_policy.csv
Deny-override rbac_model_with_deny.conf rbac_policy_with_deny.csv
Priority priority_model.conf priority_policy.csv

Middlewares

Authz middlewares for web frameworks: https://casbin.org/docs/middlewares

Our adopters

https://casbin.org/docs/adopters

Contributors

This project exists thanks to all the people who contribute.

Backers

Thank you to all our backers! ๐Ÿ™ [Become a backer]

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

License

This project is licensed under the Apache 2.0 license.

Contact

If you have any issues or feature requests, please contact us. PR is welcomed.

casbin-rs's People

Contributors

0xethsign avatar bk138 avatar chosunone avatar codeironman avatar dependabot-preview[bot] avatar devinr528 avatar divy9881 avatar drholmie avatar gopherj avatar greenhandatsjtu avatar hackerchai avatar hgz-20 avatar hsluoyz avatar kbalt avatar omid avatar psiace avatar schungx avatar selflocking avatar siddheshkanawade avatar valkum avatar worapolw avatar xcaptain avatar zupzup avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

casbin-rs's Issues

improve error handling

currently we are using:

pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

Ideally we should use our own error type.

Model proc-macro WIP example

One of the more interesting features of rust are proc-macro's, I have wondered if it was possible to use a diesel style "schema" generator to create the Model and by doing so create a type that can hold policy lines and have this also used in enforce possibly? This example is more a proof of concept to start a conversation about this feature.

I created a repo with a minimal-ish example
Casbin-proc-macro

Questions

Is it even wanted?
How would this be integrated without breaking existing code or have a simple upgrade path ?
Any other thoughts

Advantages

  • gives type safety to the model definition compile time errors, instead of runtime
  • by reading in the model we can dynamically define the Policy struct and the function to compare a request to a policy
  • no rhai this is maybe a positive not sure about the trade offs
  • maybe others?

Role inheritance problem in rbac.rs

The current implementation of role inheritance has a big problem, it can handle direct link between 2 roles but can't handle indirect links like user -> role -> role.

#[test]
fn test_role() {
    let mut rm = DefaultRoleManager::new(3);
    rm.add_link("u1", "g1", vec![]);
    rm.add_link("u2", "g1", vec![]);
    rm.add_link("u3", "g2", vec![]);
    rm.add_link("u4", "g2", vec![]);
    rm.add_link("u4", "g3", vec![]);
    rm.add_link("g1", "g3", vec![]); // problem occurs, u1 is already in g1, but can't infer that u1 is also in g3
    rm.print_roles();
}

the print result is:

{
    "g3": Role { name: "g3", roles: [] }, 
    "u2": Role { name: "u2", roles: [Role { name: "g1", roles: [] }] }, 
    "u1": Role { name: "u1", roles: [Role { name: "g1", roles: [] }] }, 
    "g1": Role { name: "g1", roles: [Role { name: "g3", roles: [] }] }, 
    "u3": Role { name: "u3", roles: [Role { name: "g2", roles: [] }] }, 
    "u4": Role { name: "u4", roles: [Role { name: "g2", roles: [] }, Role { name: "g3", roles: [] }] }, 
    "g2": Role { name: "g2", roles: [] }
}

When using has_link to check link between u1 and g3, we will iterate over roles in u1 that is g1, but this g1 has no related roles, while in fact in role manager, g1 has a related role g3.

This problem occurs because we are constructing Role using a clone

#[derive(Clone, Debug)]
pub struct Role {
    pub name: String,
    pub roles: Vec<Role>,
}

So when a role changes, it's original clone doesn't change. I have 2 ideas to solve this problem:

  1. Keep using a clone, so when g1 has changed(added to g3), all roles related with g1 must be updated, that is u1 becomes

    "u1": Role { name: "u1", roles: [Role { name: "g1", roles: [Role { name: "g3", roles: [] }] }] }, 
  2. Pass by reference using Rc

  3. Copy all_roles in DefaultRoleManager to every Role object, since all_roles has all the role infomations.

I will try the second way first. @GopherJ @XanthusL Do you have any ideas on this problem?

considering trait inheritance

Deref inheritance is an anti-pattern in rust because it's just passing actions/tasks to specific field. That means in our function we'll never be able to get child's reference.

So I propose to have better design and remove the usage of Deref in CachedEnforcer.

What can we do?
I think we can use trait inheritance, that means we should have a base/parent trait which can be called as CoreApi.

Then imple<T> InternalApi for T: CoreApi, impl<T> MgmtApi for T: InternalApi, Impl<T> RbacApi for T: MgmtApi

So finally for different types of enforcers they just need to implement CoreApi to get all the management functions.

How can we have different behaviours for different Enforcers?

No idea for now.

CachedEnforcer will have extra functions comparing to normal enforcers
SyncedEnforcer is a wrapper over Arc<RwLock<Enforcer>> or Arc<RwLock<CachedEnforcer>>

Improve casbin-rs bench

While doing the same bench test suite like casbin golang: https://github.com/casbin/casbin/blob/master/model_b_test.go

I found we can get similar results with CachedEnforcer but the normal enforcer is 6x ~ 7x slower than golang version.

Can someone help investigating?

@xcaptain @DevinR528 @PsiACE @hackerchai

eg. running small size of rbac model test:

Casbin Golang

BenchmarkRBACModelSmall-8                     	   10000	    113285 ns/op	   28528 B/op	     790 allocs/op

Casbin Rust

test b_benchmark_rbac_model_small                ... bench:     729,440 ns/iter (+/- 266,774)

Ref

Start to make examples

It'll help the developpers a lot if we can add more examples like what actix-web has done: https://github.com/actix/examples

Currently we don't have enough docs. I hope me or anyone else can find time to add them.

But at the same time we can already start to prepare some examples like:

  1. actix-web + casbin example with fileadapter
  2. actix-web + casbin example with diesel-adapter
  3. actix-web + casbin example using CachedEnforcer (default cache)
    ...

Maybe we can also add some examples on other web frameworks.

I added an repo if anyone wants to contribute.

https://github.com/casbin-rs/examples

Missing logger support

A proper logger is missing util now with the following features:

  1. show the request (sub, obj, action) etc
  2. show the enforce result
  3. show if it's cached result (by adding a prefix [CACHE])

Implement FilteredAdapter

Currently we load all the policies into casbin and then do the matching, this would cause a lot of memory when we have many policies(millions or more). A filtered adapter means when an enforce request happens then query the adapter for result.

diesel-adapter and sqlx-adapter should implement this feature.

Target casbin to wasm

We usually use casbin-rs in a web server, that is when a request comes in, a middleware or controller asks casbin whether it's authenticated. A common situation is that we also have a frontend ui that needs to do permission control. If we can compile casbin to wasm file, then we can do enforce at browser, no need to ask for the server all the time.

Rust has very good wasm support, I think this idea can be implemented. After that we need to consider how to sync user's policy from server, maybe we need to add a get_all_policies_for_user in internal_api.rs

Add actix-casbin-auth to Actix official middleware list

We have very active development for Casbin-RS now. However, our popularity is still under my estimation (129 stars). I hope we can do more advertisement in future to let people know and use our project.

The first thing we should do is to add Casbin middleware: https://github.com/actix/examples/tree/master/casbin to Actix official middleware list: https://actix.rs/docs/middleware/ . So Actix website visitors will also come to our GitHub page.

Can anyone work on this?

covert rule: Vec<&str> to rule: Vec<String>

At the beginning we would like to use &str because the conversion from String to &str is cheap.

However, if we consider the production environment and the real use case, we will find it's hard to generate &str with the "correct" lifetime ('static).

For example:

fn fetch_policies_from_db(conn: &Conn) -> Vec<&str> {
 // hard to implement because &str borrows policies (String) but policies will be released after this function call
}

The better solution is to have: Into<String> but we need to guarantee it's Send and Sync.

Missing watcher support (with a proper lock)

A general case in casbin is to share the same policy storage with several enforcers, once an CRUD operation has been made through one of the enforcers. The others need to pull these changes immediately from policy storage.

And during the execution of the pull operation, enforcer needs to be locked. No more enforce can be made util the finish of pull.

Also it'll be good if we can pull only the changes instead of reloading all the policies.

Refactor API structure

Currently, our APIs are separated in different modules, like:

use casbin_rs::model::Model;
use casbin_rs::enforcer::Enforcer;
use casbin_rs::adapter::FileAdapter;
use casbin_rs::RbacApi;

which are not convinent to use, should be exported from crate root like:

use casbin::{Model, Enforcer, FileAdapter}

casbin-raft implementation

I don't really like the idea of watchers because it's buggy and heavy, image when one enforcer AddPolicy, the others need to reload all the policies from DB.....

we should find other solutions.

See also: casbin/casbin#421

GSOC: actix actor,actix middleware

  • implement an actix casbin actor which accepts messages of rbac request (subject, domain, object, action and more). The actor will return the result of enforcing.
  • implement an actix middleware using actix-web-httpauth and actix casbin actor

Please comment below if you have sent proposal and if you'll work on this.

Missing cache support

casbin doesn't have cache support, but it'll be good to have it, especially for realtime applications.

Implement ABAC model

Current we are matching directly over object, means subject can act on object, with ABAC we can do matching like: subject can act on object is this object has specific attributes.

ABAC doc

This problem may be a little hard because we are using Vec<&str> as the parameter for enforce, which requires every element in the vector has the same type that is &str. I have 2 ideas about this issue.

  1. Change from Vec<&str> to tuple, so we don't need to worry about the type.
  2. Still passing obj as a string, parsing this string into a struct in rhai then do the matching.

GSOC: logger system

  • Design Logger trait and implement the default logger which implements Logger trait.
  • Logger can show if the enforcing result is cached or not by the prefix [Cache].
  • Logger support levels (info, debug, error...). Logs can be filtered by specific Level.
  • Logs are by default colorful.

Please comment below if you have sent proposal and if you'll work on this.

Roadmap for [email protected]

Todo list for the v1.0 release

  • Optimize model.rs, add more tests @GopherJ #8
  • Optimize rbac.rs, implement get_roles and get_users method @xcaptain #7 #9
  • Setup travis and codecov @hsluoyz
  • Check whether domain can have a length more than 2 strings. This affects if we should define domain as Option<&str> or Vec<&str> @hsluoyz
  • Implement RBAC apis for enforcer @xcaptain #14 #20
  • Add clippy to travis-ci and fix clippy warnings @GopherJ #15 #16
  • Add autoSave, autoBuildRoleLink option in enforcer @xcaptain #17
  • Investigate how to use variadic parameters in rust. e.g. enforce, generate_g_function, add_permission_for_userand so on. Currently we are using Vector to store those parameters, but this way is not elegant @xcaptain #22
  • Refactor api structure. #29
  • Rename the package name. #30
  • Default implement some APIs for Enforcer. #31
  • Create model from file directly for easy of use. #32
  • Better error handling. #24
  • Create a diesel adapter
  • Investigate how to replace a boxed trait at runtime. e.g. replace e.adapter with a new adapter at runtime. @DevinR528
  • Fix module visibility, remove unnessarry pub
  • Add benchmark @GopherJ @DevinR528
  • Investigate how to parse in operator in config file @GopherJ
  • Automatically publish package to crate.io when a new git tag is created
  • Add cache support (@GopherJ @xcaptain ) #45
  • Add watcher support (@GopherJ @xcaptain ) #46
  • Add async-std support (low priority @GopherJ @xcaptain ) #43
  • Implement a FilteredAdapter #79
  • Implement ABAC model #78
    People who want to contribute please reply to this issue, apply for a task and then make a pull request.

Consider default implement some APIs for Enforcer

Currently, we wrapped APIs in 3 traits: RbacApi, MgmtApi and InternalApi, When user want some APIs they need to manually import the corresponding trait where enforcer was created, it's not very convenient to use. We can consider using ether ways below

  1. Remove the 3 traits and directly implement these APIs to Enforcer
  2. Default implement these traits for Enforcer at lib.rs

split code into multiple features

some times users just need part of all the features. It's important to let users choose what they want so that we can decrease the binary size.

We can have the following features:

  • cached => add the dependency: ttl_cache, default enabled: false
  • glob => add the dependency: globset, default-enabled: true
  • logging => add the dependency: log , default-enabled: false, [done]
  • ip => add the dependency: ip_network, default-enabled: false
  • watcher => add EventEmitter, every policy change will need to clone the changes and pass it to watcher which added some overhead, default-enabled: false

Proper error handling (eliminate unwrap calls)

We (Devolutions) are looking at switching to the new casbin-rs project, leaving our initial port of casbin to Rust from a year ago behind in favour of this one. Overall, the new project looks good, but our primary concern is the lack of proper error handling.

A quick search in the code base reveals there are 92 unwrap() calls. Some of them are benign in nature, like unwrap calls in unit tests, but there are a bunch of them that could cause issues in production. We intend to run this code inside a server process, and unwrap() would cause the entire thread to panic, which is obviously not desirable.

Our old code had a simple error type with an enumeration of a few errors (evaluation error, parsing error, invalid value, etc.). The new code has a simple string error type, but it should be improved with a real enumeration of errors to facilitate error processing.

The idea would be to replace all unwrap() calls with errors, which would affect the function signatures (it would return a Result, which is either a success value or an error value). In addition to this, the error type would be improved with an enumeration of error types. Once functions are modified to return Results with the same error type, it should become easy to use the ? operator to reduce the amount of boilerplate error handling.

We would like to know what would work best to make these kind of improvements. Should we do it ourselves according to the way we have described it and submit a PR, or would you prefer doing it? The side effect of adding proper error handling is that changes will affect a lot of files.

Please let us know, and we'll begin putting our efforts on the new casbin-rs. Thanks!

Add a new from file api for model

Currently, we use load_model to load model definitions from a config file, which is not convenient to use (2 lines), should consider adding a new from file API so we can create a model in 1 line.

Add unit tests for `addPolicies` and `removePolicies`

Following up from #55 need to add unit tests for the following apis:

async fn add_policies(&mut self, paramss: Vec<Vec<&str>>) -> Result<bool>;
async fn remove_policies(&mut self, paramss: Vec<Vec<&str>>) -> Result<bool>;
async fn add_named_policies(&mut self, ptype: &str, paramss: Vec<Vec<&str>>) -> Result<bool>;
async fn remove_named_policies(&mut self, ptype: &str, paramss: Vec<Vec<&str>>)
        -> Result<bool>;
async fn add_grouping_policies(&mut self, paramss: Vec<Vec<&str>>) -> Result<bool>;
async fn remove_grouping_policies(&mut self, paramss: Vec<Vec<&str>>) -> Result<bool>;
async fn add_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<&str>>,
    ) -> Result<bool>;
async fn remove_named_grouping_policies(
        &mut self,
        ptype: &str,
        paramss: Vec<Vec<&str>>,
    ) -> Result<bool>;
async fn add_permissions_for_user(
        &mut self,
        user: &str,
        permissions: Vec<Vec<&str>>,
    ) -> Result<bool>;
async fn add_roles_for_user(
        &mut self,
        user: &str,
        roles: Vec<&str>,
        domain: Option<&str>,
    ) -> Result<bool>;

GSOC: Shared Redis TTL cache

  • Design Cache trait, make CachedEnforcer being able to accept external cache.
  • Implement redis TTL cache.

Please comment below if you have sent proposal and if you'll work on this.

Is this usable?

In the main repo, it says this project is "work in progress" and the crate is also not published. Does this mean it isn't usable at this point in time? If so, can you give an ETA on how long it will take? It looks like there hasn't been much activity in here lately... Thanks!

GSOC: tokio runtime, fully async adapter

  • Make async-std runtime (the current one) a feature runtime-async-std and enables it by default.
  • Implement the new feature runtime-tokio which use tokio as the executor of async/await.
  • Implement a fully asynchronous adapter: sqlx-adapter using sqlx.

Please comment below if you have sent proposal and if you'll work on this.

More examples for policies.

Hi!

Do you have an example to add new policies from a function, not just a file?
I saw that you can use a MemoryAdapter but I can't make it work.

let adapter = FileAdapter::new("/policy.csv");
let e = Enforcer::new(Box::new(model), Box::new(adapter)).await.unwrap();
let enforce = e.enforce(&["alice", "/admin/users", "GET"]).unwrap();
println!("Result: {}", enforce);

Thanks!
Lucas

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.