Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Gucin committed Jan 19, 2015
0 parents commit f74b180
Show file tree
Hide file tree
Showing 17 changed files with 417 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source 'http://rubygems.org'

gem 'oauth2'
gem 'json'
gem 'jwt'
22 changes: 22 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
GEM
remote: http://rubygems.org/
specs:
addressable (2.3.2)
faraday (0.7.6)
addressable (~> 2.2)
multipart-post (~> 1.1)
rack (~> 1.1)
json (1.7.5)
multi_json (1.3.6)
multipart-post (1.1.5)
oauth2 (0.5.2)
faraday (~> 0.7)
multi_json (~> 1.0)
rack (1.4.1)

PLATFORMS
ruby

DEPENDENCIES
json
oauth2
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## Redmine omniauth azure

This plugin is used to authenticate in redmine through Azure.
It was more than inspired by redmine_omniauth_google see https://github.com/twinslash/redmine_omniauth_google and redmine_omniauth_github see https://github.com/ares/redmine_omniauth_github

### Installation:

Choose folder /plugins, make command

```console
git clone https://github.com/Gucin/redmine_omniauth_azure.git
```

Update gems and restart rails server.

### Registration

To make possible to authenticate via Azure you must first to register application in Azure

* Go to the [registration](https://manage.windowsazure.com) link.
* Goto Application tab, click "Add"
* Choose "Add an application my organization is developing"
* Choose "Web Application/OR Web API", fill in the "Name"
* Fill in the "Sign-on URL" and "App ID URL" with your website corresponding address. Please ensure APP ID URL is unique
* Goto Configure tab, find the "CLIENT_ID"
* Generate a new key and remember the key value as "CLIENT_SECRET"
* Fill your callback url to "REPLY URI"
* Save your changes, and click "View Points" to get tenant id
* The tenant id is a unique id for each application
```
https://graph.windows.net/ff5ffbee-d997-4f49-a926-5f3c41bc45a3
tenant id is ff5ffbee-d997-4f49-a926-5f3c41bc45a3
```
The registrations is complete.

### Configuration

To make plugin to work properly

* Login as administrator. In top menu select "Administration". Choose menu item Plugins. In plugins list choose "Redmine Omniauth Azure plugin". Press "Configure".
* Fill Сlient ID & Client Secret & Tenant ID by corresponding values, obtained by Azure.
* Put the check "Oauth authentification", to make it possible to login through Azure. Click Apply. Users can now to use apportunity to login via Azure.

Additionaly
* Setup value Autologin in Settings on tab Authentification

### Other options

By default, all domains are allowed to authenticate through Azure.
To limit login through Azure for other domains you have to fill allowed domains in the text box field the "Allowed domains". Domains must be separated by newlines. For example:

```text
onedomain.com
otherdomain.com
```

### Work process

User goes to the login page (sign in) and clicks the button with Azure image. The plugin redirects him to Azure where user enters his the еmail & password from Azure. Azure redirects user back to plugins controller. Then the following cases:
1. If auto registration is enabled, user is redirected to 'my/page'
2. In other case user account is created and waited for admin activation
102 changes: 102 additions & 0 deletions app/controllers/redmine_oauth_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require 'account_controller'
require 'json'
require 'jwt'

class RedmineOauthController < AccountController
include Helpers::MailHelper
include Helpers::Checker
def oauth_azure
if Setting.plugin_redmine_omniauth_azure[:azure_oauth_authentication]
session[:back_url] = params[:back_url]
redirect_to oauth_client.auth_code.authorize_url(:redirect_uri => oauth_azure_callback_url, :scope => scopes)
else
password_authentication
end
end

def oauth_azure_callback
if params[:error]
flash[:error] = l(:notice_access_denied)
redirect_to signin_path
else
token = oauth_client.auth_code.get_token(params[:code], :redirect_uri => oauth_azure_callback_url, :resource => "00000002-0000-0000-c000-000000000000")
user_info = JWT.decode(token.token, nil, false)
logger.error user_info

email = user_info['unique_name']

if email
checked_try_to_login email, user_info
else
flash[:error] = l(:notice_no_verified_email_we_could_use)
redirect_to signin_path
end
end
end

def checked_try_to_login(email, user)
if allowed_domain_for?(email)
try_to_login email, user
else
flash[:error] = l(:notice_domain_not_allowed, :domain => parse_email(email)[:domain])
redirect_to signin_path
end
end

def try_to_login email, info
params[:back_url] = session[:back_url]
session.delete(:back_url)

user = User.find_or_initialize_by_mail(email)
if user.new_record?
# Self-registration off
redirect_to(home_url) && return unless Setting.self_registration?
# Create on the fly
user.firstname, user.lastname = info["name"].split(' ') unless info['name'].nil?
user.firstname ||= info["name"]
user.lastname ||= info["name"]
user.mail = email
user.login = info['login']
user.login ||= [user.firstname, user.lastname]*"."
user.random_password
user.register

case Setting.self_registration
when '1'
register_by_email_activation(user) do
onthefly_creation_failed(user)
end
when '3'
register_automatically(user) do
onthefly_creation_failed(user)
end
else
register_manually_by_administrator(user) do
onthefly_creation_failed(user)
end
end
else
# Existing record
if user.active?
successful_authentication(user)
else
account_pending
end
end
end

def oauth_client
@client ||= OAuth2::Client.new(settings[:client_id], settings[:client_secret],
:site => 'https://login.windows.net',
:authorize_url => '/' + settings[:tenant_id] + '/oauth2/authorize',
:token_url => '/' + settings[:tenant_id] + '/oauth2/token')
end

def settings
@settings ||= Setting.plugin_redmine_omniauth_azure
end

def scopes
'user:email'
end
end
2 changes: 2 additions & 0 deletions app/helpers/redmine_omniauth_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module RedmineOmniauthHelper
end
10 changes: 10 additions & 0 deletions app/views/hooks/_view_account_login_bottom.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<%= stylesheet_link_tag 'buttons', :plugin => 'redmine_omniauth_azure' %>

<% if Setting.plugin_redmine_omniauth_azure[:azure_oauth_authentication] %>
<%= link_to oauth_azure_path(:back_url => back_url) do %>
<%= button_tag :class => 'button-login' do %>
<%= image_tag('/plugin_assets/redmine_omniauth_azure/images/azure_login_icon.png', :class => 'button-login-icon', :alt => l(:login_via_azure)) %>
<%= content_tag :div, l(:login_via_azure), :class => 'button-login-text' %>
<% end %>
<% end %>
<% end %>
20 changes: 20 additions & 0 deletions app/views/settings/_azure_settings.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<p>
<label>Client ID:</label>
<%= text_field_tag 'settings[client_id]', @settings[:client_id] %>
</p>
<p>
<label>Client Secret:</label>
<%= text_field_tag 'settings[client_secret]', @settings[:client_secret] %>
</p>
<p>
<label>Tenant ID:</label>
<%= text_field_tag 'settings[tenant_id]', @settings[:tenant_id] %>
</p>
<p>
<label>Available domains</label>
<%= text_area_tag "settings[allowed_domains]", @settings[:allowed_domains], :rows => 5 %>
</p>
<p>
<label>Oauth authentication:</label>
<%= check_box_tag "settings[azure_oauth_authentication]", true, @settings[:azure_oauth_authentication] %>
</p>
Binary file added assets/images/azure_login_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions assets/stylesheets/buttons.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.button-login {
position: relative;
left: 45%;
display: inline-block;
border: 1px solid #999;
border-radius: 2px;
margin-top: 10px;
width: 135px;
height: 25px;
padding: 0;
}

.button-login-icon {
float: left;
height: 18px;
padding: 1px 0px 0px 4px;
}

.button-login-text {
line-height: 21px;
background-image: -webkit-linear-gradient(bottom, #ddd, white);
font-size: 12px;
}
6 changes: 6 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
en:
notice_unable_to_obtain_azure_credentials: "Unable to obtain credentials from Azure."
notice_domain_not_allowed: "You can not login using %{domain} domain."
notice_azure_access_denied: "You must allow to use you Azure credentials to enter this site."
login_via_azure: "Login via Azure"
notice_no_verified_email_we_could_use: "No verified email provided by Azure, go and verify you email address on azure.com"
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
get 'oauth_azure', :to => 'redmine_oauth#oauth_azure'
get 'oauth2callback_azure', :to => 'redmine_oauth#oauth_azure_callback', :as => 'oauth_azure_callback'
18 changes: 18 additions & 0 deletions init.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'redmine'
require_dependency 'redmine_omniauth_azure/hooks'

Redmine::Plugin.register :redmine_omniauth_azure do
name 'Redmine Omniauth Azure plugin'
author 'Gucin Tsui'
description 'This is a plugin for Redmine registration through Azure AD'
version '0.0.1'
url 'https://github.com/ares/redmine_omniauth_azure'
author_url 'https://github.com/'

settings :default => {
:client_id => "",
:client_secret => "",
:github_oauth_autentication => false,
:allowed_domains => ""
}, :partial => 'settings/azure_settings'
end
11 changes: 11 additions & 0 deletions lib/helpers/checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Helpers
module Checker
def allowed_domain_for? email
allowed_domains = Setting.plugin_redmine_omniauth_azure[:allowed_domains]
return unless allowed_domains
allowed_domains = allowed_domains.split
return true if allowed_domains.empty?
allowed_domains.index(parse_email(email)[:domain])
end
end
end
8 changes: 8 additions & 0 deletions lib/helpers/mail_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Helpers
module MailHelper
def parse_email email
email_data = email && email.is_a?(String) ? email.match(/(.*?)@(.*)/) : nil
{:login => email_data[1], :domain => email_data[2]} if email_data
end
end
end
9 changes: 9 additions & 0 deletions lib/redmine_omniauth_azure/hooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module RedmineOmniauthAzure
class Hooks < Redmine::Hook::ViewListener
def view_account_login_bottom(context = {})
context[:controller].send(:render_to_string, {
:partial => "hooks/view_account_login_bottom",
:locals => context})
end
end
end
Loading

0 comments on commit f74b180

Please sign in to comment.