Commit 8f037442b053c64ebc995e1bb95ccb045ca59f5c

Authored by Victor Costa
2 parents be8fa504 074d20a9

Merge branch 'oauth_rails3' into stable

Showing 43 changed files with 624 additions and 234 deletions   Show diff stats
plugins/oauth_client/controllers/oauth_client_plugin_admin_controller.rb
1 1 class OauthClientPluginAdminController < AdminController
2 2  
3 3 def index
4   - settings = params[:settings] || {}
  4 + end
  5 +
  6 + def new
  7 + @provider = environment.oauth_providers.new
  8 + render :file => 'oauth_client_plugin_admin/edit'
  9 + end
  10 +
  11 + def remove
  12 + environment.oauth_providers.find(params[:id]).destroy
  13 + redirect_to :action => 'index'
  14 + end
5 15  
6   - @settings = Noosfero::Plugin::Settings.new(environment, OauthClientPlugin, settings)
7   - @providers = @settings.get_setting(:providers) || {}
  16 + def edit
  17 + @provider = params[:id] ? environment.oauth_providers.find(params[:id]) : environment.oauth_providers.new
8 18 if request.post?
9   - @settings.save!
10   - session[:notice] = 'Settings succefully saved.'
11   - redirect_to :action => 'index'
  19 + if @provider.update_attributes(params['oauth_client_plugin_provider'])
  20 + session[:notice] = _('Saved!')
  21 + else
  22 + session[:notice] = _('Error!')
  23 + end
12 24 end
13 25 end
14 26  
... ...
plugins/oauth_client/controllers/public/oauth_client_plugin_public_controller.rb
... ... @@ -2,21 +2,12 @@ class OauthClientPluginPublicController &lt; PublicController
2 2  
3 3 def callback
4 4 auth = request.env["omniauth.auth"]
5   - login = auth.info.email.split('@').first
6   - user = environment.users.find_with_omniauth(auth)
7   -
8   - if user
9   - session[:user] = user
10   - redirect_to :controller => :account, :action => :login
11   - else
12   - session[:oauth_data] = auth
13   - name = auth.info.name
14   - name ||= auth.extra && auth.extra.raw_info ? auth.extra.raw_info.name : ''
15   - redirect_to :controller => :account, :action => :signup, :user => {:login => login, :email => auth.info.email}, :profile_data => {:name => name}
16   - end
  5 + user = environment.users.find_by_email(auth.info.email)
  6 + user ? login(user) : signup(auth)
17 7 end
18 8  
19 9 def failure
  10 + session[:notice] = _('Failed to login')
20 11 redirect_to root_url
21 12 end
22 13  
... ... @@ -25,4 +16,29 @@ class OauthClientPluginPublicController &lt; PublicController
25 16 redirect_to root_url
26 17 end
27 18  
  19 + protected
  20 +
  21 + def login(user)
  22 + provider = OauthClientPlugin::Provider.find(session[:provider_id])
  23 + user_provider = user.oauth_user_providers.find_by_provider_id(provider.id)
  24 + unless user_provider
  25 + user_provider = user.oauth_user_providers.create(:user => user, :provider => provider, :enabled => true)
  26 + end
  27 + if user_provider.enabled? && provider.enabled?
  28 + session[:user] = user.id
  29 + else
  30 + session[:notice] = _("Can't login with #{provider.name}")
  31 + end
  32 +
  33 + redirect_to :controller => :account, :action => :login
  34 + end
  35 +
  36 + def signup(auth)
  37 + login = auth.info.email.split('@').first
  38 + session[:oauth_data] = auth
  39 + name = auth.info.name
  40 + name ||= auth.extra && auth.extra.raw_info ? auth.extra.raw_info.name : ''
  41 + redirect_to :controller => :account, :action => :signup, :user => {:login => login, :email => auth.info.email}, :profile_data => {:name => name}
  42 + end
  43 +
28 44 end
... ...
plugins/oauth_client/db/migrate/20140828184930_add_settings_to_users.rb
... ... @@ -1,9 +0,0 @@
1   -class AddSettingsToUsers < ActiveRecord::Migration
2   - def self.up
3   - add_column :users, :settings, :string
4   - end
5   -
6   - def self.down
7   - remove_column :users, :settings
8   - end
9   -end
plugins/oauth_client/db/migrate/20141010135314_create_oauth_client_plugin_provider.rb 0 → 100644
... ... @@ -0,0 +1,19 @@
  1 +class CreateOauthClientPluginProvider < ActiveRecord::Migration
  2 +
  3 + def self.up
  4 + create_table :oauth_client_plugin_providers do |t|
  5 + t.integer :environment_id
  6 + t.string :strategy
  7 + t.string :name
  8 + t.text :options
  9 + t.boolean :enabled
  10 + t.integer :image_id
  11 +
  12 + t.timestamps
  13 + end
  14 + end
  15 +
  16 + def self.down
  17 + drop_table :oauth_client_plugin_providers
  18 + end
  19 +end
... ...
plugins/oauth_client/db/migrate/20141014162710_create_oauth_client_user_providers.rb 0 → 100644
... ... @@ -0,0 +1,14 @@
  1 +class CreateOauthClientUserProviders < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :oauth_client_plugin_user_providers do |t|
  4 + t.references :user
  5 + t.references :provider
  6 + t.boolean :enabled
  7 + t.timestamps
  8 + end
  9 + end
  10 +
  11 + def self.down
  12 + drop_table :oauth_client_plugin_user_providers
  13 + end
  14 +end
... ...
plugins/oauth_client/lib/ext/environment.rb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +require_dependency 'environment'
  2 +
  3 +class Environment
  4 +
  5 + has_many :oauth_providers, :class_name => 'OauthClientPlugin::Provider'
  6 +
  7 +end
... ...
plugins/oauth_client/lib/ext/user.rb
... ... @@ -2,21 +2,11 @@ require_dependency &#39;user&#39;
2 2  
3 3 class User
4 4  
5   - acts_as_having_settings :field => :settings
6   -
7   - settings_items :oauth_providers, :type => Array, :default => []
8   -
9   - def self.find_with_omniauth(auth)
10   - user = self.find_by_email(auth.info.email)
11   - if user && !user.oauth_providers.empty? #FIXME save new oauth providers
12   - user
13   - else
14   - nil
15   - end
16   - end
  5 + has_many :oauth_user_providers, :class_name => 'OauthClientPlugin::UserProvider'
  6 + has_many :oauth_providers, :through => :oauth_user_providers, :source => :provider
17 7  
18 8 def password_required_with_oauth?
19   - password_required_without_oauth? && oauth_providers.blank?
  9 + password_required_without_oauth? && oauth_providers.empty?
20 10 end
21 11  
22 12 alias_method_chain :password_required?, :oauth
... ... @@ -24,7 +14,12 @@ class User
24 14 after_create :activate_oauth_user
25 15  
26 16 def activate_oauth_user
27   - activate unless oauth_providers.empty?
  17 + unless oauth_providers.empty?
  18 + activate
  19 + oauth_providers.each do |provider|
  20 + OauthClientPlugin::UserProvider.create!(:user => self, :provider => provider, :enabled => true)
  21 + end
  22 + end
28 23 end
29 24  
30 25 def make_activation_code_with_oauth
... ...
plugins/oauth_client/lib/oauth_client_plugin.rb
... ... @@ -13,7 +13,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin
13 13 def login_extra_contents
14 14 plugin = self
15 15 proc do
16   - render :partial => 'auth/oauth_login', :locals => {:providers => plugin.enabled_providers}
  16 + render :partial => 'auth/oauth_login', :locals => {:providers => environment.oauth_providers.enabled}
17 17 end
18 18 end
19 19  
... ... @@ -29,12 +29,6 @@ class OauthClientPlugin &lt; Noosfero::Plugin
29 29 end
30 30 end
31 31  
32   - def enabled_providers
33   - settings = Noosfero::Plugin::Settings.new(context.environment, OauthClientPlugin)
34   - providers = settings.get_setting(:providers)
35   - providers.select {|provider, options| options[:enabled]}
36   - end
37   -
38 32 PROVIDERS = {
39 33 :facebook => {
40 34 :name => 'Facebook'
... ... @@ -55,18 +49,24 @@ class OauthClientPlugin &lt; Noosfero::Plugin
55 49  
56 50 Rails.application.config.middleware.use OmniAuth::Builder do
57 51 PROVIDERS.each do |provider, options|
58   - provider provider, :setup => lambda { |env|
  52 + setup = lambda { |env|
59 53 request = Rack::Request.new env
60 54 strategy = env['omniauth.strategy']
61 55  
62 56 domain = Domain.find_by_name(request.host)
63 57 environment = domain.environment rescue Environment.default
64   - settings = Noosfero::Plugin::Settings.new(environment, OauthClientPlugin)
65   - providers = settings.get_setting(:providers)
66 58  
67   - strategy.options.client_id = providers[provider][:client_id]
68   - strategy.options.client_secret = providers[provider][:client_secret]
69   - }, :path_prefix => '/plugin/oauth_client', :callback_path => "/plugin/oauth_client/public/callback/#{provider}"
  59 + provider_id = request.session['omniauth.params'] ? request.session['omniauth.params']['id'] : request.params['id']
  60 + provider = environment.oauth_providers.find(provider_id)
  61 + strategy.options.merge!(provider.options.symbolize_keys)
  62 +
  63 + request.session[:provider_id] = provider_id
  64 + }
  65 +
  66 + provider provider, :setup => setup,
  67 + :path_prefix => '/plugin/oauth_client',
  68 + :callback_path => "/plugin/oauth_client/public/callback/#{provider}",
  69 + :client_options => { :connection_opts => { :proxy => ENV["HTTP_PROXY"] || ENV["http_proxy"] || ENV["HTTPS_PROXY"] || ENV["https_proxy"] } }
70 70 end
71 71  
72 72 unless Rails.env.production?
... ... @@ -81,7 +81,7 @@ class OauthClientPlugin &lt; Noosfero::Plugin
81 81 auth = session[:oauth_data]
82 82  
83 83 if auth.present? && params[:user].present?
84   - params[:user][:oauth_providers] = [{:provider => auth.provider, :uid => auth.uid}]
  84 + params[:user][:oauth_providers] = [OauthClientPlugin::Provider.find(session[:provider_id])]
85 85 if request.post? && auth.info.email != params[:user][:email]
86 86 raise "Wrong email for oauth signup"
87 87 end
... ...
plugins/oauth_client/lib/oauth_client_plugin/provider.rb 0 → 100644
... ... @@ -0,0 +1,20 @@
  1 +class OauthClientPlugin::Provider < Noosfero::Plugin::ActiveRecord
  2 +
  3 + belongs_to :environment
  4 +
  5 + validates_presence_of :name, :strategy
  6 +
  7 + acts_as_having_image
  8 + acts_as_having_settings :field => :options
  9 +
  10 + settings_items :client_id, :type => :string
  11 + settings_items :client_secret, :type => :string
  12 + settings_items :client_options, :type => Hash
  13 +
  14 + attr_accessible :name, :environment, :strategy, :client_id, :client_secret, :enabled, :client_options, :image_builder
  15 +
  16 + scope :enabled, :conditions => {:enabled => true}
  17 +
  18 + acts_as_having_image
  19 +
  20 +end
... ...
plugins/oauth_client/lib/oauth_client_plugin/user_provider.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class OauthClientPlugin::UserProvider < Noosfero::Plugin::ActiveRecord
  2 +
  3 + belongs_to :user, :class_name => 'User'
  4 + belongs_to :provider, :class_name => 'OauthClientPlugin::Provider'
  5 +
  6 + set_table_name :oauth_client_plugin_user_providers
  7 +
  8 + attr_accessible :user, :provider, :enabled
  9 +
  10 +end
... ...
plugins/oauth_client/lib/omniauth/strategies/noosfero_oauth2.rb
... ... @@ -4,10 +4,9 @@ module OmniAuth
4 4 module Strategies
5 5 class NoosferoOauth2 < OmniAuth::Strategies::OAuth2
6 6 option :name, :noosfero_oauth2
7   -
8 7 option :client_options, {
9   - :site => "http://noosfero.com:3001",
10   - :authorize_url => "/oauth/authorize"
  8 + :authorize_url => '/oauth_provider/oauth/authorize',
  9 + :token_url => '/oauth_provider/oauth/token'
11 10 }
12 11  
13 12 uid { raw_info["id"] }
... ... @@ -15,15 +14,12 @@ module OmniAuth
15 14 info do
16 15 {
17 16 :email => raw_info["email"]
18   - # and anything else you want to return to your API consumers
19 17 }
20 18 end
21 19  
22 20 def raw_info
23   - #@raw_info ||= access_token.get('/api/v1/me.json').parsed
24   - #FIXME
25   - #raise access_token.inspect
26   - User['vfcosta'].attributes
  21 + #FIXME access the noosfero api (coming soon)
  22 + @raw_info ||= access_token.get('/plugin/oauth_provider/public/me').parsed
27 23 end
28 24 end
29 25 end
... ...
plugins/oauth_client/public/images/facebook-icon.png

831 Bytes

plugins/oauth_client/public/images/google-icon.png

1.58 KB

plugins/oauth_client/public/style.css
... ... @@ -5,18 +5,11 @@
5 5 display: inline-block;
6 6 text-decoration: none;
7 7 background-repeat: no-repeat;
8   - padding-left: 22px;
9 8 line-height: 20px;
10 9 }
11   -
12   -.oauth-login .provider .facebook {
13   - background-image: url(images/facebook-icon.png);
  10 +.oauth-login .provider a img {
  11 + max-width: 40px;
14 12 }
15   -
16   -.oauth-login .provider .google_oauth2 {
17   - background-image: url(images/google-icon.png);
18   -}
19   -
20 13 .oauth-login .provider .developer {
21 14 display: none;
22 15 }
... ...
plugins/oauth_client/test/functional/oauth_client_plugin_public_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,80 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class OauthClientPluginPublicControllerTest < ActionController::TestCase
  4 +
  5 + def setup
  6 + @auth = mock
  7 + @auth.stubs(:info).returns(mock)
  8 + request.env["omniauth.auth"] = @auth
  9 + @environment = Environment.default
  10 + @provider = OauthClientPlugin::Provider.create!(:name => 'provider', :strategy => 'provider', :enabled => true)
  11 + end
  12 + attr_reader :auth, :environment, :provider
  13 +
  14 + should 'redirect to signup when user is not found' do
  15 + auth.info.stubs(:email).returns("xyz123@noosfero.org")
  16 + auth.info.stubs(:name).returns('xyz123')
  17 + session[:provider_id] = provider.id
  18 +
  19 + get :callback
  20 + assert_match /.*\/account\/signup/, @response.redirect_url
  21 + end
  22 +
  23 + should 'redirect to login when user is found' do
  24 + user = fast_create(User, :environment_id => environment.id)
  25 + auth.info.stubs(:email).returns(user.email)
  26 + auth.info.stubs(:name).returns(user.name)
  27 + session[:provider_id] = provider.id
  28 +
  29 + get :callback
  30 + assert_redirected_to :controller => :account, :action => :login
  31 + assert_equal user.id, session[:user]
  32 + end
  33 +
  34 + should 'do not login when the provider is disabled' do
  35 + user = fast_create(User, :environment_id => environment.id)
  36 + auth.info.stubs(:email).returns(user.email)
  37 + auth.info.stubs(:name).returns(user.name)
  38 + session[:provider_id] = provider.id
  39 + provider.update_attribute(:enabled, false)
  40 +
  41 + get :callback
  42 + assert_redirected_to :controller => :account, :action => :login
  43 + assert_equal nil, session[:user]
  44 + end
  45 +
  46 + should 'do not login when the provider is disabled for a user' do
  47 + user = fast_create(User, :environment_id => environment.id)
  48 + auth.info.stubs(:email).returns(user.email)
  49 + auth.info.stubs(:name).returns(user.name)
  50 + session[:provider_id] = provider.id
  51 + user.oauth_user_providers.create(:user => user, :provider => provider, :enabled => false)
  52 +
  53 + get :callback
  54 + assert_redirected_to :controller => :account, :action => :login
  55 + assert_equal nil, session[:user]
  56 + end
  57 +
  58 + should 'save provider when an user login with it' do
  59 + user = fast_create(User, :environment_id => environment.id)
  60 + auth.info.stubs(:email).returns(user.email)
  61 + auth.info.stubs(:name).returns(user.name)
  62 + session[:provider_id] = provider.id
  63 +
  64 + get :callback
  65 + assert_equal [provider], user.oauth_providers
  66 + end
  67 +
  68 + should 'do not duplicate relations between an user and a provider when the same provider was used again in a login' do
  69 + user = fast_create(User, :environment_id => environment.id)
  70 + auth.info.stubs(:email).returns(user.email)
  71 + auth.info.stubs(:name).returns(user.name)
  72 + session[:provider_id] = provider.id
  73 +
  74 + get :callback
  75 + assert_no_difference 'user.oauth_user_providers.count' do
  76 + 3.times { get :callback }
  77 + end
  78 + end
  79 +
  80 +end
... ...
plugins/oauth_client/test/unit/environment_test.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +
  3 +class UserTest < ActiveSupport::TestCase
  4 +
  5 + should 'be able to add oauth providers in a environment' do
  6 + env = fast_create(Environment)
  7 + env.oauth_providers << OauthClientPlugin::Provider.new(:name => 'test', :strategy => 'test')
  8 + end
  9 +
  10 +end
... ...
plugins/oauth_client/test/unit/oauth_client_plugin_test.rb
... ... @@ -3,13 +3,16 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
3 3 class OauthClientPluginTest < ActiveSupport::TestCase
4 4  
5 5 def setup
6   - @plugin = OauthClientPlugin.new
  6 + @plugin = OauthClientPlugin.new(self)
7 7 @params = {}
8 8 @plugin.stubs(:context).returns(self)
9 9 @environment = Environment.default
  10 + @session = {}
  11 + @request = mock
  12 + @provider = OauthClientPlugin::Provider.create!(:name => 'name', :strategy => 'strategy')
10 13 end
11 14  
12   - attr_reader :params, :plugin, :environment
  15 + attr_reader :params, :plugin, :environment, :session, :request, :provider
13 16  
14 17 should 'has extra contents for login' do
15 18 assert plugin.login_extra_contents
... ... @@ -19,33 +22,27 @@ class OauthClientPluginTest &lt; ActiveSupport::TestCase
19 22 assert_equal '', instance_eval(&plugin.signup_extra_contents)
20 23 end
21 24  
22   - should 'has signup extra contents if there is enabled providers' do
23   - params[:user] = {:oauth_providers => [:provider]}
  25 + should 'has signup extra contents if oauth_data exists in session' do
  26 + session[:oauth_data] = {:oauth => 'test'}
24 27 expects(:render).with(:partial => 'account/oauth_signup').once
25 28 instance_eval(&plugin.signup_extra_contents)
26 29 end
27 30  
28   - should 'list enabled providers' do
29   - settings = Noosfero::Plugin::Settings.new(environment, OauthClientPlugin)
30   - providers = {:test => {:enabled => true}, :test2 => {:enabled => false}}
31   - settings.set_setting(:providers, providers)
32   - assert_equal({:test => {:enabled => true}}, plugin.enabled_providers)
33   - end
34   -
35 31 should 'define before filter for account controller' do
36 32 assert plugin.account_controller_filters
37 33 end
38 34  
39 35 should 'raise error if oauth email was changed' do
40   - request = mock
41   - stubs(:request).returns(request)
42 36 request.expects(:post?).returns(true)
43 37  
44 38 oauth_data = mock
45 39 info = mock
46 40 oauth_data.stubs(:info).returns(info)
  41 + oauth_data.stubs(:uid).returns('uid')
  42 + oauth_data.stubs(:provider).returns('provider')
47 43 info.stubs(:email).returns('test@example.com')
48   - stubs(:session).returns({:oauth_data => oauth_data})
  44 + session[:oauth_data] = oauth_data
  45 + session[:provider_id] = provider.id
49 46  
50 47 params[:user] = {:email => 'test2@example.com'}
51 48 assert_raises RuntimeError do
... ... @@ -54,32 +51,35 @@ class OauthClientPluginTest &lt; ActiveSupport::TestCase
54 51 end
55 52  
56 53 should 'do not raise error if oauth email was not changed' do
57   - request = mock
58   - stubs(:request).returns(request)
59 54 request.expects(:post?).returns(true)
60 55  
61 56 oauth_data = mock
62 57 info = mock
63 58 oauth_data.stubs(:info).returns(info)
  59 + oauth_data.stubs(:uid).returns('uid')
  60 + oauth_data.stubs(:provider).returns('provider')
64 61 info.stubs(:email).returns('test@example.com')
65   - stubs(:session).returns({:oauth_data => oauth_data})
  62 + session[:oauth_data] = oauth_data
  63 + session[:provider_id] = provider.id
66 64  
67 65 params[:user] = {:email => 'test@example.com'}
68 66 instance_eval(&plugin.account_controller_filters[:block])
69 67 end
70 68  
71 69 should 'do not raise error if oauth session is not set' do
72   - request = mock
73   - stubs(:request).returns(request)
74   - request.expects(:post?).returns(true)
75   - stubs(:session).returns({})
76 70 instance_eval(&plugin.account_controller_filters[:block])
77 71 end
78 72  
79 73 should 'do not raise error if it is not a post' do
80   - request = mock
81   - stubs(:request).returns(request)
82 74 request.expects(:post?).returns(false)
  75 + params[:user] = {:email => 'test2@example.com'}
  76 +
  77 + oauth_data = mock
  78 + oauth_data.stubs(:uid).returns('uid')
  79 + oauth_data.stubs(:provider).returns('provider')
  80 + session[:provider_id] = provider.id
  81 +
  82 + session[:oauth_data] = oauth_data
83 83 instance_eval(&plugin.account_controller_filters[:block])
84 84 end
85 85  
... ...
plugins/oauth_client/test/unit/user_test.rb
... ... @@ -2,22 +2,13 @@ require File.dirname(__FILE__) + &#39;/../test_helper&#39;
2 2  
3 3 class UserTest < ActiveSupport::TestCase
4 4  
5   - should 'find with omniauth params' do
6   - user = fast_create(User)
7   - user.settings[:oauth_providers] = [:test => {}]
8   - user.save!
9   - auth = {:info => OpenStruct.new({:email => user.email})}
10   - assert_equal user, User.find_with_omniauth(OpenStruct.new(auth))
11   - end
12   -
13   - should 'do not return user if there is no provider' do
14   - user = fast_create(User)
15   - auth = {:info => OpenStruct.new({:email => user.email})}
16   - assert_equal nil, User.find_with_omniauth(OpenStruct.new(auth))
  5 + def setup
  6 + @provider = OauthClientPlugin::Provider.create!(:name => 'name', :strategy => 'strategy')
17 7 end
  8 + attr_reader :provider
18 9  
19 10 should 'password is not required if there is a oauth provider' do
20   - User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [:test])
  11 + User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [provider])
21 12 end
22 13  
23 14 should 'password is required if there is a oauth provider' do
... ... @@ -27,7 +18,7 @@ class UserTest &lt; ActiveSupport::TestCase
27 18 end
28 19  
29 20 should 'activate user when created with oauth' do
30   - user = User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [:test])
  21 + user = User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [provider])
31 22 assert user.activated?
32 23 end
33 24  
... ... @@ -37,7 +28,7 @@ class UserTest &lt; ActiveSupport::TestCase
37 28 end
38 29  
39 30 should 'not make activation code when created with oauth' do
40   - user = User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [:test])
  31 + user = User.create!(:email => 'testoauth@example.com', :login => 'testoauth', :oauth_providers => [provider])
41 32 assert !user.activation_code
42 33 end
43 34  
... ...
plugins/oauth_client/views/auth/_facebook.html.erb
... ... @@ -1 +0,0 @@
1   -<a class="facebook" href="/plugin/oauth_client/facebook"><%= _('Login with Facebook') %></a>
plugins/oauth_client/views/auth/_google_oauth2.html.erb
... ... @@ -1 +0,0 @@
1   -<a class="google_oauth2" href="/plugin/oauth_client/google_oauth2"><%= _('Login with Google') %></a>
plugins/oauth_client/views/auth/_noosfero_oauth2.html.erb
... ... @@ -1 +0,0 @@
1   -<a class="noosfero_oauth2" href="/plugin/oauth_client/noosfero_oauth2"><%= _('Login with Noosfero') %></a>
plugins/oauth_client/views/auth/_oauth_login.html.erb
1 1 <div class="oauth-login">
2   - <% providers.each do |provider, options| %>
  2 + <% unless providers.empty? %>
  3 + <%= _('Login with:') %>
  4 + <% end %>
  5 + <% providers.each do |provider| %>
3 6 <span class="provider">
4   - <%= render :partial => "auth/#{provider}", :locals => {:app_id => options['client_id'] } %>
  7 + <%= link_to provider.image ? image_tag(provider.image.public_filename) : provider.name, "/plugin/oauth_client/#{provider.strategy}?id=#{provider.id}", :class => provider.strategy, :title => provider.name %>
5 8 </span>
6 9 <% end %>
7 10  
8 11 <span class="provider">
9 12 <% unless Rails.env.production? %>
10   - <%= link_to _('Developer Login'), "/plugin/oauth/developer", :class => 'developer' %>
  13 + <%= link_to _('Developer Login'), "/plugin/oauth_client/developer", :class => 'developer' %>
11 14 <% end %>
12 15 </span>
13 16 </div>
... ...
plugins/oauth_client/views/oauth_client_plugin_admin/_noosfero_oauth2.html.erb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +<%= f.fields_for :client_options, OpenStruct.new(provider.options[:client_options]) do |c| %>
  2 + <div class="client-url">
  3 + <span class="label"><%= _('Client Url') %></span>
  4 + <span class="value"><%= c.text_field :site %></span>
  5 + </div>
  6 +<% end %>
... ...
plugins/oauth_client/views/oauth_client_plugin_admin/edit.html.erb 0 → 100644
... ... @@ -0,0 +1,60 @@
  1 +<h1><%= _('Oauth Client Settings') %></h1>
  2 +<h3><%= _('Edit Provider') %></h3>
  3 +
  4 +<%= form_for @provider, :url => {:action => 'edit'}, :method => 'post' do |f| %>
  5 +
  6 + <div class="enabled">
  7 + <%= f.check_box :enabled %>
  8 + <%= _('Enabled') %>
  9 + </div>
  10 +
  11 + <div class="name">
  12 + <span class="label">
  13 + <%= _('Name') %>
  14 + </span>
  15 + <span class="value">
  16 + <%= f.text_field :name %>
  17 + </span>
  18 + </div>
  19 +
  20 + <div class="strategy">
  21 + <span class="label">
  22 + <%= _('Strategy') %>
  23 + </span>
  24 + <span class="value">
  25 + <%= f.select :strategy, OauthClientPlugin::PROVIDERS %>
  26 + </span>
  27 + </div>
  28 +
  29 + <div class="image-icon">
  30 + <%= f.fields_for :image_builder, @provider.image do |i| %>
  31 + <%= file_field_or_thumbnail(_('Image:'), @provider.image, i) %><%= _("Max size: %s (.jpg, .gif, .png)")% Image.max_size.to_humanreadable %>
  32 + <% end %>
  33 + </div>
  34 +
  35 + <div class="client-id">
  36 + <span class="label">
  37 + <%= _('Client Id') %>
  38 + </span>
  39 + <span class="value">
  40 + <%= f.text_field :client_id %>
  41 + </span>
  42 + </div>
  43 +
  44 + <div class="client-secret">
  45 + <span class="label">
  46 + <%= _('Client Secret') %>
  47 + </span>
  48 + <span class="value">
  49 + <%= f.text_field :client_secret %>
  50 + </span>
  51 + </div>
  52 +
  53 + <% if File.exists?(File.join(File.dirname(__FILE__), "_#{@provider.strategy}.html.erb")) %>
  54 + <%= render :partial => "#{@provider.strategy}", :locals => {:f => f, :provider => @provider} %>
  55 + <% end %>
  56 +
  57 + <% button_bar do %>
  58 + <%= submit_button(:save, _('Save'), :cancel => {:action => 'index'}) %>
  59 + <% end %>
  60 +<% end %>
... ...
plugins/oauth_client/views/oauth_client_plugin_admin/index.html.erb
1 1 <h1><%= _('Oauth Client Settings') %></h1>
  2 +<h3><%= _('Providers') %></h3>
  3 +<%= link_to _('New'), {:action => 'new'} %>
  4 +<table>
  5 + <tr>
  6 + <th><%= _('Name') %></th>
  7 + <th><%= _('Strategy') %></th>
  8 + <th><%= _('Actions') %></th>
  9 + </tr>
2 10  
3   -<%= form_for(:settings) do |f| %>
4   - <div class="providers">
5   - <h3><%= _('Providers') %></h3>
6   - <%= f.fields_for :providers, OpenStruct.new(@providers) do |p| %>
7   -
8   - <% OauthClientPlugin::PROVIDERS.each do |available_provider, options| %>
9   - <% provider = OpenStruct.new(@providers[available_provider]) %>
10   -
11   - <%= p.fields_for available_provider, provider do |o| %>
12   - <div class="provider">
13   - <div class="name">
14   - <h4><%= o.check_box :enabled, {:class => 'enable', :checked => provider.enabled=='true'}, true, false %>
15   - <%= options[:name] %></h4>
16   - </div>
17   - <div class="options" style="<%= provider.enabled=='true' ? '':'display:none' %>">
18   - <div class="client-id">
19   - <span class="label"><%= _('Client ID') %></span>
20   - <span class="value"><%= o.text_field :client_id %></span>
21   - </div>
22   - <div class="client-secret">
23   - <span class="label"><%= _('Client Secret') %></span>
24   - <span class="value"><%= o.text_field :client_secret %></span>
25   - </div>
26   - </div>
27   - </div>
28   - <% end %>
29   - <% end %>
  11 + <% environment.oauth_providers.each do |provider| %>
  12 + <tr>
  13 + <td><%= provider.name %></td>
  14 + <td><%= provider.strategy %></td>
  15 + <td>
  16 + <%= link_to _('Edit'), {:action => 'edit', :id => provider.id} %>
  17 + <%= link_to _('Remove'), {:action => 'remove', :id => provider.id} %>
  18 + </td>
  19 + </tr>
30 20 <% end %>
31   -
32   - <% button_bar do %>
33   - <%= submit_button(:save, _('Save'), :cancel => {:controller => 'plugins', :action => 'index'}) %>
34   - <% end %>
35   - </div>
36   -<% end %>
37   -
38   -<script>
39   - jQuery(document).ready(function($) {
40   - $('.providers .provider .enable').on('click', function() {
41   - $(this).parents('.provider').find('.options').toggle('fast');
42   - });
43   - });
44   -</script>
  21 +</table>
... ...
plugins/oauth_provider/controllers/doorkeeper/application_controller.rb 0 → 100644
... ... @@ -0,0 +1,8 @@
  1 +module Doorkeeper
  2 + class ApplicationController < ApplicationController
  3 +
  4 + include Helpers::Controller
  5 + helper 'doorkeeper/form_errors'
  6 +
  7 + end
  8 +end
... ...
plugins/oauth_provider/controllers/oauth_provider_applications_controller.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class OauthProviderApplicationsController < Doorkeeper::ApplicationsController
  2 +
  3 + no_design_blocks
  4 + layout :get_layout
  5 +
  6 + def show
  7 + end
  8 +
  9 +end
... ...
plugins/oauth_provider/controllers/oauth_provider_authorized_applications_controller.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +class OauthProviderAuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
  2 +
  3 + no_design_blocks
  4 + layout :get_layout
  5 +
  6 +end
... ...
plugins/oauth_provider/controllers/oauth_provider_plugin_admin_controller.rb 0 → 100644
... ... @@ -0,0 +1,6 @@
  1 +class OauthProviderPluginAdminController < AdminController
  2 +
  3 + def index
  4 + end
  5 +
  6 +end
... ...
plugins/oauth_provider/controllers/public/oauth_provider_plugin_public_controller.rb 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +class OauthProviderPluginPublicController < PublicController
  2 +
  3 + doorkeeper_for :me
  4 +
  5 + def me
  6 + user = User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token
  7 + render :json => {:id =>user.login, :email => user.email}.to_json
  8 + end
  9 +
  10 +end
... ...
plugins/oauth_provider/lib/oauth_provider_plugin.rb
... ... @@ -9,18 +9,14 @@ class OauthProviderPlugin &lt; Noosfero::Plugin
9 9 end
10 10  
11 11 Doorkeeper.configure do
12   - # Change the ORM that doorkeeper will use.
13   - # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
14 12 orm :active_record
15 13  
16   - # This block will be called to check whether the resource owner is authenticated or not.
17 14 resource_owner_authenticator do
18 15 domain = Domain.find_by_name(request.host)
19 16 environment = domain ? domain.environment : Environment.default
20 17 environment.users.find_by_id(session[:user]) || redirect_to('/account/login')
21 18 end
22 19  
23   - # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
24 20 admin_authenticator do
25 21 domain = Domain.find_by_name(request.host)
26 22 environment = domain ? domain.environment : Environment.default
... ... @@ -31,81 +27,24 @@ class OauthProviderPlugin &lt; Noosfero::Plugin
31 27 user
32 28 end
33 29  
34   - # Authorization Code expiration time (default 10 minutes).
35   - # authorization_code_expires_in 10.minutes
36   -
37   - # Access token expiration time (default 2 hours).
38   - # If you want to disable expiration, set this to nil.
39   - # access_token_expires_in 2.hours
40   -
41   - # Reuse access token for the same resource owner within an application (disabled by default)
42   - # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
43   - # reuse_access_token
44   -
45   - # Issue access tokens with refresh token (disabled by default)
46   - # use_refresh_token
47   -
48   - # Provide support for an owner to be assigned to each registered application (disabled by default)
49   - # Optional parameter :confirmation => true (default false) if you want to enforce ownership of
50   - # a registered application
51   - # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support
52   - # enable_application_owner :confirmation => false
53   -
54   - # Define access token scopes for your provider
55   - # For more information go to
56   - # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes
57   - # default_scopes :public
58   - # optional_scopes :write, :update
59   -
60   - # Change the way client credentials are retrieved from the request object.
61   - # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
62   - # falls back to the `:client_id` and `:client_secret` params from the `params` object.
63   - # Check out the wiki for more information on customization
64   - # client_credentials :from_basic, :from_params
65   -
66   - # Change the way access token is authenticated from the request object.
67   - # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then
68   - # falls back to the `:access_token` or `:bearer_token` params from the `params` object.
69   - # Check out the wiki for more information on customization
70   - # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
71   -
72   - # Change the native redirect uri for client apps
73   - # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider
74   - # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL
75   - # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi)
76   - #
77   - # native_redirect_uri 'urn:ietf:wg:oauth:2.0:oob'
78   -
79   - # Specify what grant flows are enabled in array of Strings. The valid
80   - # strings and the flows they enable are:
81   - #
82   - # "authorization_code" => Authorization Code Grant Flow
83   - # "implicit" => Implicit Grant Flow
84   - # "password" => Resource Owner Password Credentials Grant Flow
85   - # "client_credentials" => Client Credentials Grant Flow
86   - #
87   - # If not specified, Doorkeeper enables all the four grant flows.
88   - #
89   - # grant_flows %w(authorization_code implicit password client_credentials)
90   -
91   - # Under some circumstances you might want to have applications auto-approved,
92   - # so that the user skips the authorization step.
93   - # For example if dealing with trusted a application.
94   - # skip_authorization do |resource_owner, client|
95   - # client.superapp? or resource_owner.admin?
96   - # end
97   -
98   - # WWW-Authenticate Realm (default "Doorkeeper").
99   - # realm "Doorkeeper"
100   -
101   - # Allow dynamic query parameters (disabled by default)
102   - # Some applications require dynamic query parameters on their request_uri
103   - # set to true if you want this to be allowed
104   - # wildcard_redirect_uri false
  30 + default_scopes :public
105 31 end
106 32  
107   - Rails.application.routes.prepend do
108   - use_doorkeeper
  33 + Rails.configuration.to_prepare do
  34 + Rails.application.routes.prepend do
  35 + scope 'oauth_provider' do
  36 + use_doorkeeper do
  37 + controllers ({
  38 + :applications => 'oauth_provider_applications',
  39 + :authorized_applications => 'oauth_provider_authorized_applications'
  40 + })
  41 + end
  42 + end
  43 + end
109 44 end
110 45  
  46 + SCOPE_TRANSLATION = {
  47 + 'public' => _('Access your public data')
  48 + }
  49 +
111 50 end
... ...
plugins/oauth_provider/views/doorkeeper/applications/_delete_form.html.erb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<%- submit_btn_css ||= 'btn btn-link' %>
  2 +<%= form_tag [:oauth, application] do %>
  3 + <input type="hidden" name="_method" value="delete">
  4 + <%= submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css %>
  5 +<% end %>
... ...
plugins/oauth_provider/views/doorkeeper/applications/_form.html.erb 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +<%= form_for [:oauth, application], html: {class: 'form-horizontal', role: 'form'} do |f| %>
  2 + <% if application.errors.any? %>
  3 + <div class="alert alert-danger" data-alert>
  4 + <p><%= _('Whoops! Check your form for possible errors') %></p>
  5 + </div>
  6 + <% end %>
  7 +
  8 + <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do %>
  9 + <%= f.label :name, class: 'col-sm-2 control-label', for: 'application_name' %>
  10 + <div class="col-sm-10">
  11 + <%= f.text_field :name, class: 'form-control' %>
  12 + <%= doorkeeper_errors_for application, :name %>
  13 + </div>
  14 + <% end %>
  15 +
  16 + <%= content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do %>
  17 + <%= f.label :redirect_uri, class: 'col-sm-2 control-label', for: 'application_redirect_uri' %>
  18 + <div class="col-sm-10">
  19 + <%= f.text_area :redirect_uri, class: 'form-control' %>
  20 + <%= doorkeeper_errors_for application, :redirect_uri %>
  21 + <span class="help-block">
  22 + <%= _('Use one line per URI') %>
  23 + </span>
  24 + <% if Doorkeeper.configuration.native_redirect_uri %>
  25 + <span class="help-block">
  26 + Use <code><%= Doorkeeper.configuration.native_redirect_uri %></code> for local tests
  27 + </span>
  28 + <% end %>
  29 + </div>
  30 + <% end %>
  31 +
  32 + <div class="form-group">
  33 + <div class="col-sm-offset-2 col-sm-10">
  34 + <%= f.submit _('Submit'), class: "btn btn-primary" %>
  35 + <%= link_to _("Cancel"), oauth_applications_path, :class => "btn btn-default" %>
  36 + </div>
  37 + </div>
  38 +<% end %>
  39 +
... ...
plugins/oauth_provider/views/doorkeeper/applications/edit.html.erb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<div class="page-header">
  2 + <h1><%= _('Edit application') %></h1>
  3 +</div>
  4 +
  5 +<%= render 'form', application: @application %>
... ...
plugins/oauth_provider/views/doorkeeper/applications/index.html.erb 0 → 100644
... ... @@ -0,0 +1,26 @@
  1 +<div class="page-header">
  2 + <h3><%= link_to _('Oauh Provider'), '/admin/plugin/oauth_provider' %></h3>
  3 +</div>
  4 +
  5 +<p><%= link_to _('New Application'), new_oauth_application_path, class: 'btn btn-success' %></p>
  6 +
  7 +<table class="table table-striped">
  8 + <thead>
  9 + <tr>
  10 + <th><%= _('Name') %></th>
  11 + <th><%= _('Callback URL') %></th>
  12 + <th></th>
  13 + <th></th>
  14 + </tr>
  15 + </thead>
  16 + <tbody>
  17 + <% @applications.each do |application| %>
  18 + <tr id="application_<%= application.id %>">
  19 + <td><%= link_to application.name, [:oauth, application] %></td>
  20 + <td><%= application.redirect_uri %></td>
  21 + <td><%= link_to _('Edit'), edit_oauth_application_path(application), class: 'btn btn-link' %></td>
  22 + <td><%= render 'delete_form', application: application %></td>
  23 + </tr>
  24 + <% end %>
  25 + </tbody>
  26 +</table>
... ...
plugins/oauth_provider/views/doorkeeper/applications/new.html.erb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<div class="page-header">
  2 + <h1>New application</h1>
  3 +</div>
  4 +
  5 +<%= render 'form', application: @application %>
... ...
plugins/oauth_provider/views/doorkeeper/applications/show.html.erb 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +<div class="page-header">
  2 + <h1><%= _('Application: %s' % @application.name) %></h1>
  3 +</div>
  4 +
  5 +<div class="row">
  6 + <div class="col-md-8">
  7 + <h4><%= _('Application Id:') %></h4>
  8 +
  9 + <p><code id="application_id"><%= @application.uid %></code></p>
  10 +
  11 + <h4><%= _('Secret:') %></h4>
  12 +
  13 + <p><code id="secret"><%= @application.secret %></code></p>
  14 +
  15 + <h4><%= _('Callback urls:') %></h4>
  16 +
  17 + <table>
  18 + <% @application.redirect_uri.split.each do |uri| %>
  19 + <tr>
  20 + <td>
  21 + <code><%= uri %></code>
  22 + </td>
  23 + <td>
  24 + </td>
  25 + </tr>
  26 + <% end %>
  27 + </table>
  28 + </div>
  29 +
  30 + <div class="col-md-4">
  31 + <h3><%= _('Actions') %></h3>
  32 +
  33 + <p>
  34 + <%= link_to _('Edit'), edit_oauth_application_path(@application), class: 'btn btn-primary' %>
  35 + <%= link_to _("Cancel"), oauth_applications_path, :class => "btn btn-default" %>
  36 + </p>
  37 +
  38 + <p><%= render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger' %></p>
  39 + </div>
  40 +</div>
... ...
plugins/oauth_provider/views/doorkeeper/authorizations/error.html.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<div class="page-header">
  2 + <h1>An error has occurred</h1>
  3 +</div>
  4 +
  5 +<main role="main">
  6 + <pre><%= @pre_auth.error_response.body[:error_description] %></pre>
  7 +</main>
... ...
plugins/oauth_provider/views/doorkeeper/authorizations/new.html.erb 0 → 100644
... ... @@ -0,0 +1,40 @@
  1 +<header class="page-header" role="banner">
  2 + <h1><%= _('Authorize required') %></h1>
  3 +</header>
  4 +
  5 +<main role="main">
  6 + <p class="h4">
  7 + <%= _('Authorize %s to use your account?' % "<strong class=\"text-info\">#{@pre_auth.client.name}</strong>") %>
  8 + </p>
  9 +
  10 + <% if @pre_auth.scopes %>
  11 + <div id="oauth-permissions">
  12 + <p><%= _('This application will be able to:') %></p>
  13 +
  14 + <ul class="text-info">
  15 + <% @pre_auth.scopes.each do |scope| %>
  16 + <li><%= OauthProviderPlugin::SCOPE_TRANSLATION[scope] %></li>
  17 + <% end %>
  18 + </ul>
  19 + </div>
  20 + <% end %>
  21 +
  22 + <div class="actions">
  23 + <%= form_tag oauth_authorization_path, method: :post do %>
  24 + <%= hidden_field_tag :client_id, @pre_auth.client.uid %>
  25 + <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri %>
  26 + <%= hidden_field_tag :state, @pre_auth.state %>
  27 + <%= hidden_field_tag :response_type, @pre_auth.response_type %>
  28 + <%= hidden_field_tag :scope, @pre_auth.scope %>
  29 + <%= submit_tag _("Authorize"), class: "btn btn-success btn-lg btn-block" %>
  30 + <% end %>
  31 + <%= form_tag oauth_authorization_path, method: :delete do %>
  32 + <%= hidden_field_tag :client_id, @pre_auth.client.uid %>
  33 + <%= hidden_field_tag :redirect_uri, @pre_auth.redirect_uri %>
  34 + <%= hidden_field_tag :state, @pre_auth.state %>
  35 + <%= hidden_field_tag :response_type, @pre_auth.response_type %>
  36 + <%= hidden_field_tag :scope, @pre_auth.scope %>
  37 + <%= submit_tag _("Deny"), class: "btn btn-danger btn-lg btn-block" %>
  38 + <% end %>
  39 + </div>
  40 +</main>
... ...
plugins/oauth_provider/views/doorkeeper/authorizations/show.html.erb 0 → 100644
... ... @@ -0,0 +1,7 @@
  1 +<header class="page-header">
  2 + <h1>Authorization code:</h1>
  3 +</header>
  4 +
  5 +<main role="main">
  6 + <code id="authorization_code"><%= params[:code] %></code>
  7 +</main>
... ...
plugins/oauth_provider/views/doorkeeper/authorized_applications/_delete_form.html.erb 0 → 100644
... ... @@ -0,0 +1,5 @@
  1 +<%- submit_btn_css ||= 'btn btn-link' %>
  2 +<%= form_tag oauth_authorized_application_path(application) do %>
  3 + <input type="hidden" name="_method" value="delete">
  4 + <%= submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: submit_btn_css %>
  5 +<% end %>
... ...
plugins/oauth_provider/views/doorkeeper/authorized_applications/index.html.erb 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +<header class="page-header">
  2 + <h1>Your authorized applications</h1>
  3 +</header>
  4 +
  5 +<main role="main">
  6 + <table class="table table-striped">
  7 + <thead>
  8 + <tr>
  9 + <th>Application</th>
  10 + <th>Created At</th>
  11 + <th></th>
  12 + <th></th>
  13 + </tr>
  14 + </thead>
  15 + <tbody>
  16 + <% @applications.each do |application| %>
  17 + <tr>
  18 + <td><%= application.name %></td>
  19 + <td><%= application.created_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
  20 + <td><%= render 'delete_form', application: application %></td>
  21 + </tr>
  22 + <% end %>
  23 + </tbody>
  24 + </table>
  25 +</main>
... ...
plugins/oauth_provider/views/oauth_provider_plugin_admin/index.html.erb 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +<div class="oauth-provider">
  2 +<h3><%= _('Oauh Provider') %></h3>
  3 +
  4 + <div class="applications">
  5 + <%= link_to _('Applications'), oauth_applications_path %>
  6 + </div>
  7 + <div class="authorized-applications">
  8 + <%= link_to _('Authorized Applications'), oauth_authorized_applications_path %>
  9 + </div>
  10 +
  11 +</div>
... ...