GithubHelp home page GithubHelp logo

fphilipe / warden-github-rails Goto Github PK

View Code? Open in Web Editor NEW
99.0 3.0 17.0 141 KB

Use GitHub as authorization and more. Use organizations and teams as means of authorization by simply wrapping your rails routes in a block. Also useful to get a user's details through OAuth.

License: MIT License

Ruby 90.90% HTML 9.10%
authentication authorization github oauth warden warden-github ruby gem

warden-github-rails's Introduction

warden-github-rails

Build Status Gem Version Dependency Status Code Climate Coverage Status

A gem for rails that provides easy GitHub OAuth integration. It is built on top of warden-github, which gives you an easy to use warden strategy to authenticate GitHub users.

Motivation

Wouldn't it be nice to

  • use your organization and its teams for user access control?
  • add a new employee to your GitHub organization or team in order to grant them access to your app's admin area?

The motivation for this gem was to provide a very easy authorization (not authentication) mechanism to existing rails apps for admins, especially in combination with organization and team memberships. The provided routing helpers do exactly that. They allow you to restrict access to members of your organization or a certain team.

This is how your rails routes.rb could look like:

constraints(subdomain: 'admin') do
  github_authenticate(org: 'my_company_inc') do
    resources :users
    resources :projects

    github_authenticated(team: 'sysadmins') do
      resource :infrastructure
    end
  end
end

Of course, this gem can also be used for user registration and authentication. Several helper methods are available in the controller to accomplish this:

class UsersController < ApplicationController
  # ...

  def new
    github_authenticate! # Performs OAuth flow when not logged in.
    @user = User.new(name: github_user.name, email: github_user.email)
  end

  def create
    attrs = params.require(:user).permit(:name, :email).merge(github_id: github_user.id)
    @user = User.create(attrs)

    if @user.persisted?
      redirect_to :show
    else
      render :new
    end
  end

  # ...
end

Example App

This repository includes an example app in example/. To play with it, follow these steps:

  1. Create an OAuth application in your GitHub settings. Set the callback URL to http://localhost:3000/

  2. Check out this repo and run:

    $ bundle
    $ cd example
    $ GITHUB_CLIENT_ID=your_id_from_step1 GITHUB_CLIENT_SECRET=your_secret_from_step1 bundle exec rails s
    
  3. Point your browser to http://localhost:3000/ and enjoy!

Installation

To use this gem, add it to your Gemfile:

gem 'warden-github-rails'

If you're using devise, make sure to use version 2.2.4 or newer. Previous versions are not compatible with warden-github-rails and thus will not work. See the note at Using alongside Devise and other Warden Gems for an explanation.

Usage

Configuration

First off, you might want to configure this gem by creating an initializer such as config/initializers/warden_github_rails.rb. There you can define:

  • various scopes and their configs (scopes are types of users with different configs)
  • the default scope (which is :user by default)
  • team aliases (GitHub teams are identified by a numerical ID; defining an alias for a team makes it easier to use)

Here's how such a config might look like:

Warden::GitHub::Rails.setup do |config|
  config.add_scope :user,  client_id:     'foo',
                           client_secret: 'bar',
                           scope:         'user:email'

  config.add_scope :admin, client_id:     'abc',
                           client_secret: 'xyz',
                           redirect_uri:  '/admin/login/callback',
                           scope:         'read:org'

  config.default_scope = :admin

  config.add_team :marketing, 456
end

For a list of allowed config parameters to use in #add_scope, read the warden-github documentation.

Inside routes.rb

The available routing helpers are defined and documented in lib/warden/github/rails/routes.rb. They all accept an optional scope that, when omitted, falls back to the default_scope configured in the initializer.

Examples:

# Performs login if not logged in already.
github_authenticate do
  resource :profile
end

# Does not perform login when not logged in.
github_authenticated do
  delete '/logout' => 'sessions#delete'
end

# Only matches when not logged in. Does not perform login.
github_unauthenticated do
  resource :registration
end

# Only matches when member of the organization. Initiates login if not logged in.
github_authenticate(org: 'my_company') do
  resource :admin
end

# Only matches when member of the team. Does not initiate login if not logged in.
github_authenticated(team: 'markting') do
  get '/dashboard' => 'dashboard#show'
end

# Matches if a member of any of the teams given. Does not initiate login if not logged in.
github_authenticated(team: ['markting', 'graphic-design']) do
  get '/dashboard' => 'dashboard#show'
end

# Using dynamic membership values:
github_authenticate(org: lambda { |req| req.params[:id] }) do
  get '/orgs/:id' => 'orgs#show'
end

Inside a Controller

The available controller helpers are defined and documented in lib/warden/github/rails/controller_helpers.rb. They all accept an optional scope that, when omitted, falls back to the default_scope configured in the initializer.

class SomeController < ActionController::Base
  def show
    @is_admin = github_authenticated?(:admin)
  end

  def delete
    github_logout
    redirect_to '/'
  end

  def settings
    github_authenticate!
    @settings = UserSettings.find_by(github_user_id: github_user.id)
  end

  def finish_wizard
    github_session[:wizard_completed] = true
  end

  def followers
    @followers = github_user.api.followers
  end
end

Communicating with the GitHub API

Once a user is logged in, you'll have access to it in the controller using github_user. It is an instance of Warden::GitHub::User which is defined in the warden-github gem. The instance has several methods to access user information such as #name, #id, #email, etc. It also features a method #api which returns a preconfigured Octokit client for that user.

Test Helpers

This gems comes with a couple test helpers to make your life easier:

  • A method is added to Rack::Response called #github_oauth_redirect? which returns true if the response is a redirect to a url that starts with https://github.com/login/oauth/authorize. You can use it in your request tests to make sure the OAuth dance is initiated. In rspec you could verify this as follows:

    subject { get '/some-url-that-triggers-oauth' }
    it { is_expected.to be_github_oauth_redirect }
  • A mock user that allows you to stub team and organization memberships:

    user = Warden::GitHub::Rails::TestHelpers::MockUser.new
    user.stub_membership(team: [234, 987], org: 'some-inc')
    user.team_member?(234) # => true
    user.organization_member?('rails') # => false
  • A method that creates a mock user and logs it in. If desired, the scope can be specified. The method returns the mock user so that you can manipulate it further:

    user = github_login(:admin)
    
    get '/org/rails/admin'
    expect(response).to be_not_found
    
    user.stub_membership(org: 'rails')
    get '/org/rails/admin'
    expect(response).to be_ok

In order to use the mock user and the #github_login method, make sure to require 'warden/github/rails/test_helpers' and to include Warden::GitHub::Rails::TestHelpers in your tests.

Using alongside Devise and other Warden Gems

Currently this gem does not play nicely with other gems that setup a warden middleware. The reason is that warden simply does not have support for multiple middlewares. The warden middleware configures a warden instance and adds it to the rack environment. Any other warden middleware downstream checks for any existing warden instance in the environment and, if present, skips itself. I've opened an issue on the warden repository to discuss possible workarounds.

Nevertheless, this gem is compatible with devise for version 2.2.4 and newer. devise allows you to specify a block that will be invoked when the warden middleware is configured. This functionality is used in this gem in order to setup the github strategy for warden instead of inserting our own middleware.

Additional Information

Dependencies

Author

Philipe Fatio (@fphilipe)

Support via Gittip

License

MIT License. Copyright 2013 Philipe Fatio

warden-github-rails's People

Contributors

atmos avatar bitdeli-chef avatar fphilipe avatar fredar avatar indirect 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

Watchers

 avatar  avatar  avatar

warden-github-rails's Issues

Best Practices When Using Route Constraints

Using this gem for internal organization apps with easy GitHub authentication. Because of such, almost all of the apps routes are in constraint helpers like github_authenticated. I have found that this works against us in a few ways. Most notably, when using url helpers in mailers - specifically sent in the background with ActiveJob.

Is there a pattern to help with this? Only one I could think if was to not use constraint helpers and instead use an application filter. My experience with this gem is only in a recent Rails 5 edge app. I suspect behavior is the same for other versions too.

Production Issues

Are there any modules that need to be included anywhere or that would differ between RAILS_ENV?

My app is working great in development but throw errors when deployed to production.

Started GET "/" for 204.195.75.101 at 2013-03-12 01:27:09 -0700
Processing by HomeController#index as HTML
Completed 500 Internal Server Error in 3ms

NameError (undefined local variable or method `github_user' for #<HomeController:0x0000000715bd30>):
  app/controllers/home_controller.rb:3:in `index'

Warden::GitHub::User object being serialized as a string

My routes.rb:

Rails.application.routes.draw do

  root 'dashboard#index'

  github_authenticate(org: '<my org here>') do
    get 'admin/index'
  end

  get '/login', to: 'sessions#create', as: :login
  get '/logout', to: 'sessions#destroy', as: :logout
end

The error I get when visiting /admin/index:

undefined method `organization_member?' for #<String:0x00000107240f10>
(lib/warden/github/rails/routes.rb:63)

My solution: Put this in a rails initializer such as config/initializers/warden/session_serializer.rb:

module Warden
  class SessionSerializer
    def user_serialize(key)
      key.to_json
    end

    def user_deserialize(key)
      user = JSON.parse(key)
      Warden::GitHub::User.new(user["attribs"], user["token"])
    end
  end
end

This probably isn't the preferred solution, though, so I didn't try to fit this change into the gem itself.

Integration Testing & Serialization of MockUser

I've noticed that if when I create a Warden::GitHub::Rails::TestHelpers::MockUser and pass that to login_as and visit my page, it works fine. However, only when I call #stub_membership with something like org: 'customink', it blows up with:

NoMethodError: undefined method `{"team"=>[], "org"=>["customink"]}=' for #<Warden::GitHub::User:0x007ff4091a2a20>
    warden-github (1.2.0) lib/warden/github/user.rb:22:in `block in marshal_load'
    warden-github (1.2.0) lib/warden/github/user.rb:22:in `each'
    warden-github (1.2.0) lib/warden/github/user.rb:22:in `marshal_load'
    warden-github (1.2.0) lib/warden/github/verifier.rb:18:in `block in deserialize'
    warden-github (1.2.0) lib/warden/github/verifier.rb:17:in `tap'
    warden-github (1.2.0) lib/warden/github/verifier.rb:17:in `deserialize'
    warden-github (1.2.0) lib/warden/github/verifier.rb:9:in `load'

Still trying to figure this out.

Issue with revoking of user tokens

Hey,

Not sure if this is the right place for this, but I'm having some issues with how the app handles user tokens being revoked.

I am using this gem for auth for an internal tool, sometimes we need to revoke the tokens. However the next time the user loads the app they get an error page and the following error is thrown:

GET https://api.github.com/user/orgs: 401 - Bad credentials // See: https://developer.github.com/v3

Once the cookies are cleared it works again, but this can become tedious with multiple users, using this gem on multiple apps.

Is this a bug in the code or have I just failed to handle this case correctly?

Thanks,
Matthew

Testing and Rails 4

I've been trying to use the test helpers in rails 4 with rspec-rails and no matter how I create the user my first request is always a 302 to github.

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.