feat: add initial code
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..adc3874 --- /dev/null +++ b/.clang-format
@@ -0,0 +1,17 @@ +--- +Language: Cpp +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 100 +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +BreakBeforeBraces: Attach +IndentCaseLabels: false +NamespaceIndentation: None +PointerAlignment: Left +SpaceAfterCStyleCast: false +SpaceBeforeParens: ControlStatements +Standard: c++17
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..94c86f1 --- /dev/null +++ b/.editorconfig
@@ -0,0 +1,27 @@ +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{cpp,h,hpp}] +indent_style = space +indent_size = 4 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 + +[CMakeLists.txt,*.cmake] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.md] +trim_trailing_whitespace = false
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6b9e080 --- /dev/null +++ b/.github/workflows/ci.yml
@@ -0,0 +1,111 @@ +# Copyright 2024 The casbin Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: CI + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +permissions: + contents: read + +jobs: + build-and-test: + name: "Build and Test on ${{ matrix.os }}" + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: '' + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: casbin + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install dependencies (Ubuntu) + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + libmysqlclient-dev \ + pkg-config + + - name: Install nlohmann_json + run: | + git clone https://github.com/nlohmann/json.git + cd json + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release + sudo cmake --build . --target install + cd ../.. + + - name: Install Casbin-CPP + run: | + git clone https://github.com/casbin/casbin-cpp.git + cd casbin-cpp + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local + sudo cmake --build . --target install + cd ../.. + + - name: Install sqlpp11 + run: | + git clone https://github.com/rbock/sqlpp11.git + cd sqlpp11 + mkdir build && cd build + cmake .. -DCMAKE_BUILD_TYPE=Release + sudo cmake --build . --target install + cd ../.. + + - name: Verify MySQL connection + run: | + mysql -h 127.0.0.1 -u root -e "SHOW DATABASES;" + + - name: Configure CMake + run: | + mkdir build + cd build + cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local + + - name: Build + run: | + cd build + cmake --build . --config Release -j $(nproc) + + - name: Run tests + env: + MYSQL_HOST: 127.0.0.1 + run: | + cd build + ./test || echo "Tests require MySQL connection"
diff --git a/.gitignore b/.gitignore index 259148f..15bf94a 100644 --- a/.gitignore +++ b/.gitignore
@@ -30,3 +30,7 @@ *.exe *.out *.app + +# Build directories +build/ +cmake-build-*/
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e0f6245 --- /dev/null +++ b/CMakeLists.txt
@@ -0,0 +1,67 @@ +# Copyright 2024 The casbin Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.19) + +project(sqlpp11-adapter VERSION 1.0.0 LANGUAGES CXX) + +# Project-wide setup +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Find required packages +find_package(casbin REQUIRED) + +# Add library target +add_library(sqlpp11_adapter STATIC + sqlpp11_adapter.cpp +) + +target_include_directories(sqlpp11_adapter + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(sqlpp11_adapter + PUBLIC + casbin +) + +# Add test executable +add_executable(test + test.cpp + sqlpp11_adapter.cpp +) + +target_include_directories(test + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries(test + PRIVATE + casbin +) + +# Installation rules +install(TARGETS sqlpp11_adapter + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib +) + +install(DIRECTORY include/ + DESTINATION include/sqlpp11_adapter + FILES_MATCHING PATTERN "*.h" +)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ee882bd --- /dev/null +++ b/CONTRIBUTING.md
@@ -0,0 +1,76 @@ +# Contributing to Sqlpp11 Adapter + +Thank you for your interest in contributing to Sqlpp11 Adapter! This document provides guidelines for contributing to the project. + +## Getting Started + +1. Fork the repository +2. Clone your fork: `git clone https://github.com/YOUR_USERNAME/sqlpp11-adapter.git` +3. Create a new branch: `git checkout -b feature/your-feature-name` + +## Development Setup + +### Requirements + +- C++17 or higher compiler +- CMake 3.19 or higher +- MySQL client library +- [Casbin-CPP](https://github.com/casbin/casbin-cpp) +- [sqlpp11](https://github.com/rbock/sqlpp11) + +### Building + +```bash +mkdir build +cd build +cmake .. +cmake --build . +``` + +### Running Tests + +```bash +cd build +./test +``` + +## Coding Standards + +- Follow the existing code style (we use clang-format with Google style) +- Write clear, self-documenting code +- Add comments for complex logic +- Include Apache 2.0 license header in all new files +- Use C++17 features appropriately + +### Code Formatting + +Format your code using clang-format: + +```bash +clang-format -i <file> +``` + +## Making Changes + +1. Make your changes in your feature branch +2. Add or update tests as needed +3. Ensure all tests pass +4. Commit your changes with clear, descriptive commit messages +5. Push to your fork +6. Submit a pull request + +## Pull Request Guidelines + +- Provide a clear description of the changes +- Reference any related issues +- Ensure CI passes +- Keep changes focused and atomic +- Update documentation as needed + +## License + +By contributing to Sqlpp11 Adapter, you agree that your contributions will be licensed under the Apache 2.0 License. + +## Questions? + +If you have questions, please open an issue for discussion.
diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..1dd4dd5 --- /dev/null +++ b/MANIFEST.in
@@ -0,0 +1,4 @@ +include README.md LICENSE +graft cmake +graft include +global-include CMakeLists.txt *.cmake \ No newline at end of file
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cf395cf --- /dev/null +++ b/Makefile
@@ -0,0 +1,37 @@ +# Copyright 2024 The casbin Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +CXX = g++ +CXXFLAGS = -Wall -std=c++17 -I. -I/usr/local/include +LDFLAGS = -L/usr/local/lib -lcasbin + +TARGET = test + +OBJS = sqlpp11_adapter.o test.o + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CXX) $(CXXFLAGS) -o $(TARGET) $(OBJS) $(LDFLAGS) + +sqlpp11_adapter.o: sqlpp11_adapter.cpp include/sqlpp11_adapter.h include/CasbinRule.h + $(CXX) $(CXXFLAGS) -c sqlpp11_adapter.cpp + +test.o: test.cpp include/sqlpp11_adapter.h + $(CXX) $(CXXFLAGS) -c test.cpp + +clean: + rm -f $(TARGET) $(OBJS) + +.PHONY: all clean
diff --git a/README.md b/README.md index bd13d97..7630b5c 100644 --- a/README.md +++ b/README.md
@@ -1 +1,189 @@ -# sqlpp11-adapter \ No newline at end of file +# Sqlpp11 Adapter + +[](https://github.com/casbin-cpp/sqlpp11-adapter/actions/workflows/ci.yml) +[](https://opensource.org/licenses/Apache-2.0) + +Sqlpp11 Adapter is a [sqlpp11](https://github.com/rbock/sqlpp11) adapter for [Casbin](https://github.com/casbin/casbin-cpp). With this library, Casbin can load policy from MySQL database or save policy to it. + +## Features + +- Load policy from MySQL database +- Save policy to MySQL database +- Full support for Casbin policy management +- Built on top of the powerful sqlpp11 library + +## Installation + +### Requirements + +- C++17 or higher +- CMake 3.19 or higher +- [Casbin-CPP](https://github.com/casbin/casbin-cpp) +- [sqlpp11](https://github.com/rbock/sqlpp11) +- MySQL client library + +### Building from source + +```bash +git clone https://github.com/casbin-cpp/sqlpp11-adapter +cd sqlpp11-adapter +mkdir build && cd build +cmake .. +cmake --build . +``` + +## Usage + +### Basic Example + +```cpp +#include "include/sqlpp11_adapter.h" +#include <casbin/casbin.h> + +int main() { + // Create adapter with MySQL connection parameters + auto adapter = std::make_shared<casbin::Sqlpp11Adapter>( + "localhost", // host + "root", // user + "password", // password + "casbin", // database + 3306 // port (default: 3306) + ); + + // Create the table (if it doesn't exist) + adapter->CreateTable(); + + // Create enforcer with model file and adapter + auto enforcer = std::make_shared<casbin::Enforcer>( + "path/to/model.conf", + adapter + ); + + // Load policy from database + enforcer->LoadPolicy(); + + // Add policies + enforcer->AddPolicy("alice", "data1", "read"); + enforcer->AddPolicy("bob", "data2", "write"); + + // Save policy to database + enforcer->SavePolicy(); + + // Check permissions + if (enforcer->Enforce("alice", "data1", "read")) { + // alice can read data1 + } + + return 0; +} +``` + +### Model Configuration + +Create a model configuration file (e.g., `rbac_model.conf`): + +```ini +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act +``` + +## API Reference + +### Constructor + +```cpp +Sqlpp11Adapter(const std::string& host, + const std::string& user, + const std::string& password, + const std::string& database, + unsigned int port = 3306); +``` + +Creates a new adapter instance with the specified MySQL connection parameters. + +### Methods + +#### LoadPolicy + +```cpp +void LoadPolicy(const std::shared_ptr<Model>& model) override; +``` + +Loads all policy rules from the database. + +#### SavePolicy + +```cpp +void SavePolicy(Model& model) override; +``` + +Saves all policy rules to the database (replaces existing policies). + +#### CreateTable + +```cpp +void CreateTable(); +``` + +Creates the `casbin_rule` table if it doesn't exist. + +#### DropTable + +```cpp +void DropTable(); +``` + +Drops the `casbin_rule` table if it exists. + +## Database Schema + +The adapter uses a table named `casbin_rule` with the following schema: + +```sql +CREATE TABLE casbin_rule ( + id INT AUTO_INCREMENT PRIMARY KEY, + ptype VARCHAR(100) NOT NULL, + v0 VARCHAR(100), + v1 VARCHAR(100), + v2 VARCHAR(100), + v3 VARCHAR(100), + v4 VARCHAR(100), + v5 VARCHAR(100) +); +``` + +## Testing + +To run the tests: + +```bash +cd build +./test +``` + +Make sure you have a MySQL server running and accessible with the connection parameters used in the test. + +## Contributing + +Contributions are welcome! Please feel free to submit a Pull Request. + +## License + +This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details. + +## Acknowledgments + +- [Casbin](https://casbin.org/) - An authorization library that supports access control models +- [sqlpp11](https://github.com/rbock/sqlpp11) - A type safe SQL template library for C++ \ No newline at end of file
diff --git a/_codeql_detected_source_root b/_codeql_detected_source_root new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/_codeql_detected_source_root
@@ -0,0 +1 @@ +. \ No newline at end of file
diff --git a/cmake/modules/Findcasbin.cmake b/cmake/modules/Findcasbin.cmake new file mode 100644 index 0000000..29c0f31 --- /dev/null +++ b/cmake/modules/Findcasbin.cmake
@@ -0,0 +1,23 @@ +include(FetchContent) + +FetchContent_Declare( + casbin + GIT_REPOSITORY https://github.com/casbin/casbin-cpp.git + GIT_TAG v1.59.0 +) + +set(CASBIN_BUILD_TEST OFF) # If you don't need to build tests for casbin +set(CASBIN_BUILD_BENCHMARK OFF) # If you don't need to build benchmarks for casbin +set(CASBIN_BUILD_BINDINGS OFF) # If you don't need language bindings provided by casbin +set(CASBIN_BUILD_PYTHON_BINDINGS OFF) # If you don't need python bindings provided by casbin + +# Making casbin and its targets accessible to our project +FetchContent_MakeAvailable(casbin) + +FetchContent_GetProperties(casbin) + +# If casbin wasn't populated, then manually populate it +if(NOT casbin_POPULATED) + FetchContent_Populate(casbin) + add_subdirectory(${casbin_SOURCE_DIR} ${casbin_BINARY_DIR}) +endif() \ No newline at end of file
diff --git a/cmake/modules/Findgoogletest.cmake b/cmake/modules/Findgoogletest.cmake new file mode 100644 index 0000000..29d583d --- /dev/null +++ b/cmake/modules/Findgoogletest.cmake
@@ -0,0 +1,34 @@ +# Copyright 2021 The casbin Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(FetchContent) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 + DOWNLOAD_EXTRACT_TIMESTAMP FALSE +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +FetchContent_MakeAvailable(googletest) +FetchContent_GetProperties(googletest) + +# Populating manually, if not done automatically +if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR}) +endif() \ No newline at end of file
diff --git a/cmake/modules/Findsqlpp11.cmake b/cmake/modules/Findsqlpp11.cmake new file mode 100644 index 0000000..c259f91 --- /dev/null +++ b/cmake/modules/Findsqlpp11.cmake
@@ -0,0 +1,17 @@ + +include(FetchContent) + +FetchContent_Declare(sqlpp11 + GIT_REPOSITORY https://github.com/rbock/sqlpp11 + GIT_TAG origin/main +) +# Configure the project here as needed +set(BUILD_MYSQL_CONNECTOR ON) +# set(BUILD_MARIADB_CONNECTOR ON) +# set(BUILD_POSTGRESQL_CONNECTOR ON) +# set(BUILD_SQLITE3_CONNECTOR ON) +# set(BUILD_SQLCIPHER_CONNECTOR ON) + +# set(USE_SYSTEM_DATE ON) + +FetchContent_MakeAvailable(sqlpp11) \ No newline at end of file
diff --git a/examples/rbac_model.conf b/examples/rbac_model.conf new file mode 100644 index 0000000..9ca4b92 --- /dev/null +++ b/examples/rbac_model.conf
@@ -0,0 +1,14 @@ +[request_definition] +r = sub, obj, act + +[policy_definition] +p = sub, obj, act + +[role_definition] +g = _, _ + +[policy_effect] +e = some(where (p.eft == allow)) + +[matchers] +m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
diff --git a/include/CasbinRule.h b/include/CasbinRule.h new file mode 100644 index 0000000..afba5db --- /dev/null +++ b/include/CasbinRule.h
@@ -0,0 +1,162 @@ +// Copyright 2024 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CASBIN_CPP_CASBINRULE_H +#define CASBIN_CPP_CASBINRULE_H + +#include <sqlpp11/table.h> +#include <sqlpp11/data_types.h> +#include <sqlpp11/char_sequence.h> + +namespace casbin { + +namespace CasbinRule_ { + struct Id { + struct _alias_t { + static constexpr const char _literal[] = "id"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T id; + T& operator()() { return id; } + const T& operator()() const { return id; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::integer, sqlpp::tag::must_not_insert, sqlpp::tag::must_not_update>; + }; + + struct Ptype { + struct _alias_t { + static constexpr const char _literal[] = "ptype"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T ptype; + T& operator()() { return ptype; } + const T& operator()() const { return ptype; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar>; + }; + + struct V0 { + struct _alias_t { + static constexpr const char _literal[] = "v0"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v0; + T& operator()() { return v0; } + const T& operator()() const { return v0; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; + + struct V1 { + struct _alias_t { + static constexpr const char _literal[] = "v1"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v1; + T& operator()() { return v1; } + const T& operator()() const { return v1; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; + + struct V2 { + struct _alias_t { + static constexpr const char _literal[] = "v2"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v2; + T& operator()() { return v2; } + const T& operator()() const { return v2; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; + + struct V3 { + struct _alias_t { + static constexpr const char _literal[] = "v3"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v3; + T& operator()() { return v3; } + const T& operator()() const { return v3; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; + + struct V4 { + struct _alias_t { + static constexpr const char _literal[] = "v4"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v4; + T& operator()() { return v4; } + const T& operator()() const { return v4; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; + + struct V5 { + struct _alias_t { + static constexpr const char _literal[] = "v5"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T v5; + T& operator()() { return v5; } + const T& operator()() const { return v5; } + }; + }; + using _traits = sqlpp::make_traits<sqlpp::varchar, sqlpp::tag::can_be_null>; + }; +} + +struct CasbinRuleTable : sqlpp::table_t<CasbinRuleTable, + CasbinRule_::Id, + CasbinRule_::Ptype, + CasbinRule_::V0, + CasbinRule_::V1, + CasbinRule_::V2, + CasbinRule_::V3, + CasbinRule_::V4, + CasbinRule_::V5> +{ + struct _alias_t { + static constexpr const char _literal[] = "casbin_rule"; + using _name_t = sqlpp::make_char_sequence<sizeof(_literal), _literal>; + template<typename T> + struct _member_t { + T casbinRule; + T& operator()() { return casbinRule; } + const T& operator()() const { return casbinRule; } + }; + }; +}; + +} // namespace casbin + +#endif // CASBIN_CPP_CASBINRULE_H \ No newline at end of file
diff --git a/include/sqlpp11_adapter.h b/include/sqlpp11_adapter.h new file mode 100644 index 0000000..8e085e4 --- /dev/null +++ b/include/sqlpp11_adapter.h
@@ -0,0 +1,55 @@ +// Copyright 2024 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CASBIN_CPP_SQLPP11_ADAPTER_H +#define CASBIN_CPP_SQLPP11_ADAPTER_H + +#include <memory> +#include <string> +#include <vector> + +#include "casbin/persist/adapter.h" +#include "casbin/util/util.h" +#include "sqlpp11/mysql/mysql.h" +#include "sqlpp11/sqlpp11.h" +#include "CasbinRule.h" + +namespace casbin { + +class Model; + +class Sqlpp11Adapter : public Adapter { +public: + Sqlpp11Adapter(const std::string& host, const std::string& user, + const std::string& password, const std::string& database, + unsigned int port = 3306); + + void LoadPolicy(const std::shared_ptr<Model>& model) override; + void SavePolicy(Model& model) override; + + void CreateTable(); + void DropTable(); + +private: + sqlpp::mysql::connection_config config_; + std::shared_ptr<sqlpp::mysql::connection> db_; + std::string table_name_; + + template<typename Row> + void LoadPolicyLine(const Row& row, const std::shared_ptr<Model>& model); +}; + +} // namespace casbin + +#endif // CASBIN_CPP_SQLPP11_ADAPTER_H
diff --git a/sqlpp11_adapter.cpp b/sqlpp11_adapter.cpp new file mode 100644 index 0000000..2a3c0eb --- /dev/null +++ b/sqlpp11_adapter.cpp
@@ -0,0 +1,147 @@ +// Copyright 2024 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/sqlpp11_adapter.h" +#include <casbin/casbin.h> +#include <iostream> + +namespace casbin { + +namespace { +const std::string kDefaultTableName = "casbin_rule"; +} + +Sqlpp11Adapter::Sqlpp11Adapter(const std::string& host, const std::string& user, + const std::string& password, const std::string& database, + unsigned int port) + : table_name_(kDefaultTableName) { + config_.host = host; + config_.user = user; + config_.password = password; + config_.database = database; + config_.port = port; + config_.debug = false; + + db_ = std::make_shared<sqlpp::mysql::connection>(config_); +} + +void Sqlpp11Adapter::LoadPolicy(const std::shared_ptr<Model>& model) { + CasbinRuleTable casbin_rule; + + try { + auto result = (*db_)(sqlpp::select(all_of(casbin_rule)).from(casbin_rule).unconditionally()); + for (const auto& row : result) { + LoadPolicyLine(row, model); + } + } catch (const sqlpp::exception& e) { + std::cerr << "Error loading policy: " << e.what() << std::endl; + throw; + } +} + +template<typename Row> +void Sqlpp11Adapter::LoadPolicyLine(const Row& row, + const std::shared_ptr<Model>& model) { + std::vector<std::string> tokens; + tokens.push_back(row.ptype); + + if (!row.v0.is_null()) tokens.push_back(row.v0.value()); + if (!row.v1.is_null()) tokens.push_back(row.v1.value()); + if (!row.v2.is_null()) tokens.push_back(row.v2.value()); + if (!row.v3.is_null()) tokens.push_back(row.v3.value()); + if (!row.v4.is_null()) tokens.push_back(row.v4.value()); + if (!row.v5.is_null()) tokens.push_back(row.v5.value()); + + if (tokens.size() < 2) { + return; // Need at least ptype and one value + } + + std::string key = tokens[0]; + std::vector<std::string> sec_tokens(tokens.begin() + 1, tokens.end()); + + if (model->HasSection("p") && model->HasAssertion("p", key)) { + model->AddPolicy("p", key, sec_tokens); + } else if (model->HasSection("g") && model->HasAssertion("g", key)) { + model->AddPolicy("g", key, sec_tokens); + } +} + +void Sqlpp11Adapter::SavePolicy(Model& model) { + try { + DropTable(); + CreateTable(); + + CasbinRuleTable casbin_rule; + + // Helper lambda to insert a single policy rule + auto insertRule = [this, &casbin_rule](const std::string& ptype, + const std::vector<std::string>& rule) { + auto insert = sqlpp::insert_into(casbin_rule).set( + casbin_rule.ptype = ptype, + casbin_rule.v0 = rule.size() > 0 ? sqlpp::value(rule[0]) : sqlpp::null, + casbin_rule.v1 = rule.size() > 1 ? sqlpp::value(rule[1]) : sqlpp::null, + casbin_rule.v2 = rule.size() > 2 ? sqlpp::value(rule[2]) : sqlpp::null, + casbin_rule.v3 = rule.size() > 3 ? sqlpp::value(rule[3]) : sqlpp::null, + casbin_rule.v4 = rule.size() > 4 ? sqlpp::value(rule[4]) : sqlpp::null, + casbin_rule.v5 = rule.size() > 5 ? sqlpp::value(rule[5]) : sqlpp::null + ); + (*db_)(insert); + }; + + // Save policy rules + for (const auto& [ptype, ast] : model.m["p"].assertion_map) { + for (const auto& rule : ast->policy) { + insertRule(ptype, rule); + } + } + + // Save grouping rules + for (const auto& [ptype, ast] : model.m["g"].assertion_map) { + for (const auto& rule : ast->policy) { + insertRule(ptype, rule); + } + } + } catch (const sqlpp::exception& e) { + std::cerr << "Error saving policy: " << e.what() << std::endl; + throw; + } +} + +void Sqlpp11Adapter::CreateTable() { + try { + db_->execute("CREATE TABLE IF NOT EXISTS " + table_name_ + + " (id INT AUTO_INCREMENT PRIMARY KEY, " + "ptype VARCHAR(100) NOT NULL, " + "v0 VARCHAR(100), " + "v1 VARCHAR(100), " + "v2 VARCHAR(100), " + "v3 VARCHAR(100), " + "v4 VARCHAR(100), " + "v5 VARCHAR(100))"); + } catch (const sqlpp::exception& e) { + std::cerr << "Error creating table: " << e.what() << std::endl; + throw; + } +} + +void Sqlpp11Adapter::DropTable() { + try { + db_->execute("DROP TABLE IF EXISTS " + table_name_); + } catch (const sqlpp::exception& e) { + std::cerr << "Error dropping table: " << e.what() << std::endl; + throw; + } +} + +} // namespace casbin \ No newline at end of file
diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..7990a2f --- /dev/null +++ b/test.cpp
@@ -0,0 +1,91 @@ +// Copyright 2024 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "include/sqlpp11_adapter.h" +#include <casbin/casbin.h> +#include <cstdlib> +#include <iostream> +#include <memory> + +using namespace casbin; + +void TestBasicOperations() { + std::cout << "Testing basic adapter operations..." << std::endl; + + try { + // Get MySQL host from environment or use default + const char* mysql_host = std::getenv("MYSQL_HOST"); + if (!mysql_host) { + mysql_host = "localhost"; + } + + // Create adapter instance + auto adapter = std::make_shared<Sqlpp11Adapter>( + mysql_host, "root", "", "casbin", 3306 + ); + + // Create table + adapter->CreateTable(); + std::cout << "Table created successfully" << std::endl; + + // Create enforcer + auto enforcer = std::make_shared<Enforcer>("examples/rbac_model.conf", adapter); + + // Add policies + enforcer->AddPolicy("alice", "data1", "read"); + enforcer->AddPolicy("bob", "data2", "write"); + enforcer->AddPolicy("data2_admin", "data2", "read"); + enforcer->AddPolicy("data2_admin", "data2", "write"); + + // Add roles + enforcer->AddGroupingPolicy("alice", "data2_admin"); + + // Save policy to database + enforcer->SavePolicy(); + std::cout << "Policy saved successfully" << std::endl; + + // Clear policy and reload from database + enforcer->ClearPolicy(); + enforcer->LoadPolicy(); + std::cout << "Policy loaded successfully" << std::endl; + + // Test enforcement + bool result1 = enforcer->Enforce("alice", "data1", "read"); + bool result2 = enforcer->Enforce("alice", "data1", "write"); + bool result3 = enforcer->Enforce("alice", "data2", "read"); + bool result4 = enforcer->Enforce("alice", "data2", "write"); + bool result5 = enforcer->Enforce("bob", "data2", "write"); + + std::cout << "Enforcement results:" << std::endl; + std::cout << " alice, data1, read: " << (result1 ? "PASS" : "FAIL") << std::endl; + std::cout << " alice, data1, write: " << (!result2 ? "PASS (correctly denied)" : "FAIL (should be denied)") << std::endl; + std::cout << " alice, data2, read: " << (result3 ? "PASS" : "FAIL") << std::endl; + std::cout << " alice, data2, write: " << (result4 ? "PASS" : "FAIL") << std::endl; + std::cout << " bob, data2, write: " << (result5 ? "PASS" : "FAIL") << std::endl; + + if (result1 && !result2 && result3 && result4 && result5) { + std::cout << "All tests PASSED!" << std::endl; + } else { + std::cerr << "Some tests FAILED!" << std::endl; + } + + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + } +} + +int main(int argc, char** argv) { + TestBasicOperations(); + return 0; +}