From 2004fe0e4d02a43ca3f7b4728c28b1c501285a9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kan=20Nyl=C3=A9n?= Date: Tue, 24 Mar 2020 17:41:20 +0100 Subject: [PATCH] added first things for lucky_can --- .editorconfig | 9 ++++++ .gitignore | 9 ++++++ .travis.yml | 6 ++++ LICENSE | 21 +++++++++++++ README.md | 54 ++++++++++++++++++++++++++++++++ shard.yml | 9 ++++++ spec/lucky_can_spec.cr | 9 ++++++ spec/spec_helper.cr | 2 ++ src/lucky_can.cr | 5 +++ src/lucky_can/base_policy.cr | 37 ++++++++++++++++++++++ src/lucky_can/forbidden_error.cr | 17 ++++++++++ 11 files changed, 178 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 shard.yml create mode 100644 spec/lucky_can_spec.cr create mode 100644 spec/spec_helper.cr create mode 100644 src/lucky_can.cr create mode 100644 src/lucky_can/base_policy.cr create mode 100644 src/lucky_can/forbidden_error.cr diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..163eb75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*.cr] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0bbd4a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/docs/ +/lib/ +/bin/ +/.shards/ +*.dwarf + +# Libraries don't need dependency lock +# Dependencies will be locked in applications that use them +/shard.lock diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..765f0e9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: crystal + +# Uncomment the following if you'd like Travis to run specs and check code formatting +# script: +# - crystal spec +# - crystal tool format --check diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3923dd9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 Håkan Nylén + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb251f3 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# lucky_can + +An little nicer way to handle authorization rules for your lucky app + +## Installation + +1. Add the dependency to your `shard.yml`: + + ```yaml + dependencies: + lucky_can: + github: confact/lucky_can + ``` + +2. Run `shards install` + +## Usage + +1. ```crystal + require "lucky_can" + ``` + +2. create an `policies` directory where all your policies will be. Now you will Add an require for that directory to the app.cr file before `pages` require. Add this: `require "./policies/**"` + +3. now you can create your policies in `policies` directory. + + +### Simple usage + +```crystal +class TeamPolicy < LuckyCan::BasePolicy + can show, team, current_user do + return false if current_user.nil? + team.users.include?(current_user) + end +end +``` + +this generate following methods for you to use by an macro: +* `TeamPolicy.show?(team, current_user)` - for simple bool check if the user have access to the team. +* `TeamPolicy.show_not_found?(team, current_user, context)` - Return an Lucky::RouteNotFoundError if the code in the block return false. +* `TeamPolicy.show_forbidden?(team, current_user, context)` - Return an LuckyCan::ForbiddenError if the code in the block return false. + +## Contributing + +1. Fork it () +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Add some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create a new Pull Request + +## Contributors + +- [Håkan Nylén](https://github.com/confact) - creator and maintainer diff --git a/shard.yml b/shard.yml new file mode 100644 index 0000000..52c4e93 --- /dev/null +++ b/shard.yml @@ -0,0 +1,9 @@ +name: lucky_can +version: 0.1.0 + +authors: + - Håkan Nylén + +crystal: 0.33.0 + +license: MIT diff --git a/spec/lucky_can_spec.cr b/spec/lucky_can_spec.cr new file mode 100644 index 0000000..0665ce4 --- /dev/null +++ b/spec/lucky_can_spec.cr @@ -0,0 +1,9 @@ +require "./spec_helper" + +describe LuckyCan do + # TODO: Write tests + + it "works" do + false.should eq(true) + end +end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr new file mode 100644 index 0000000..4c71d26 --- /dev/null +++ b/spec/spec_helper.cr @@ -0,0 +1,2 @@ +require "spec" +require "../src/lucky_can" diff --git a/src/lucky_can.cr b/src/lucky_can.cr new file mode 100644 index 0000000..604de0f --- /dev/null +++ b/src/lucky_can.cr @@ -0,0 +1,5 @@ +require "./lucky_can/**" + +module LuckyCan + VERSION = "0.1.0" +end diff --git a/src/lucky_can/base_policy.cr b/src/lucky_can/base_policy.cr new file mode 100644 index 0000000..a3e9bda --- /dev/null +++ b/src/lucky_can/base_policy.cr @@ -0,0 +1,37 @@ +class LuckyCan::BasePolicy + def self.forbidden(context, &block) + unless yield + raise LuckyCan::ForbiddenError.new(context) + return false + end + true + end + + def self.not_found(context, &block) + unless yield + raise Lucky::RouteNotFoundError.new(context) + return false + end + true + end + + macro can(name, *methods) + def self.{{name}}?({% for method in methods %}{{method.id}},{% end %}) : Bool + {{yield}} + rescue Avram::RecordNotFoundError + false + end + + def self.{{name}}_forbidden?({% for method in methods %}{{method.id}},{% end %} context) : Bool + forbidden(context) do + {{name}}?({% for method in methods %}{{method.id}},{% end %}) + end + end + + def self.{{name}}_not_found?({% for method in methods %}{{method.id}},{% end %} context) : Bool + not_found(context) do + {{name}}?({% for method in methods %}{{method.id}},{% end %}) + end + end + end +end diff --git a/src/lucky_can/forbidden_error.cr b/src/lucky_can/forbidden_error.cr new file mode 100644 index 0000000..7ccd74a --- /dev/null +++ b/src/lucky_can/forbidden_error.cr @@ -0,0 +1,17 @@ +class LuckyCan::ForbiddenError < Lucky::Error + include Lucky::RenderableError + + getter context + + def initialize(@context : HTTP::Server::Context) + super "Forbidden" + end + + def renderable_status : Int32 + 403 + end + + def renderable_message : String + "You have no access to this. Sorry!" + end +end