From a64aff2f1c1ddc77b00211489d74fbc23c0c2fa2 Mon Sep 17 00:00:00 2001 From: Florian Unglaub <florian.unglaub@nix-wie-weg.de> Date: Fri, 3 Aug 2012 17:27:39 +0200 Subject: [PATCH] Omniauth Support --- Gemfile | 4 ++ Gemfile.lock | 32 +++++++++++++++ app/assets/stylesheets/auth_methods.scss | 10 +++++ app/assets/stylesheets/main.scss | 26 ++++++------ .../omniauth_callbacks_controller.rb | 32 ++++++++++++++- app/helpers/application_helper.rb | 11 +++-- app/views/devise/sessions/new.html.erb | 13 ++++-- app/views/layouts/profile.html.haml | 2 +- app/views/profile/password.html.haml | 38 ++++++++++++------ app/views/profile/show.html.haml | 7 ++++ config/gitlab.yml.example | 22 +++++++--- config/initializers/1_settings.rb | 19 ++++++--- ...803152018_add_provider_and_uid_to_users.rb | 6 +++ db/schema.rb | 32 ++++++++------- .../assets/images/authbuttons/github_32.png | Bin 0 -> 1931 bytes .../assets/images/authbuttons/github_64.png | Bin 0 -> 4447 bytes .../assets/images/authbuttons/google_32.png | Bin 0 -> 1615 bytes .../assets/images/authbuttons/google_64.png | Bin 0 -> 3446 bytes .../assets/images/authbuttons/twitter_32.png | Bin 0 -> 1439 bytes .../assets/images/authbuttons/twitter_64.png | Bin 0 -> 3384 bytes 20 files changed, 194 insertions(+), 60 deletions(-) create mode 100644 app/assets/stylesheets/auth_methods.scss create mode 100644 db/migrate/20120803152018_add_provider_and_uid_to_users.rb create mode 100644 vendor/assets/images/authbuttons/github_32.png create mode 100644 vendor/assets/images/authbuttons/github_64.png create mode 100644 vendor/assets/images/authbuttons/google_32.png create mode 100644 vendor/assets/images/authbuttons/google_64.png create mode 100644 vendor/assets/images/authbuttons/twitter_32.png create mode 100644 vendor/assets/images/authbuttons/twitter_64.png diff --git a/Gemfile b/Gemfile index d2a5728f97c5a..12610b6582118 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,10 @@ gem "mysql2" # Auth gem "devise", "~> 2.1.0" +gem 'omniauth' +gem 'omniauth-google-oauth2' +gem 'omniauth-twitter' +gem 'omniauth-github' # GITLAB patched libs gem "grit", :git => "https://github.com/gitlabhq/grit.git", :ref => "7f35cb98ff17d534a07e3ce6ec3d580f67402837" diff --git a/Gemfile.lock b/Gemfile.lock index 7356c35ede085..b34f401b1c4c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -166,6 +166,8 @@ GEM eventmachine (0.12.10) execjs (1.4.0) multi_json (~> 1.0) + faraday (0.8.1) + multipart-post (~> 1.1) ffaker (1.14.0) ffi (1.0.11) foreman (0.47.0) @@ -191,6 +193,7 @@ GEM httparty (0.8.3) multi_json (~> 1.0) multi_xml + httpauth (0.1) i18n (0.6.0) journey (1.0.4) jquery-rails (2.0.2) @@ -200,6 +203,8 @@ GEM jquery-rails railties (>= 3.1.0) json (1.7.4) + jwt (0.1.5) + multi_json (>= 1.0) kaminari (0.13.0) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -223,12 +228,35 @@ GEM sprockets (~> 2.0) multi_json (1.3.6) multi_xml (0.5.1) + multipart-post (1.1.5) mysql2 (0.3.11) net-ldap (0.2.2) nokogiri (1.5.3) + oauth (0.4.6) + oauth2 (0.8.0) + faraday (~> 0.8) + httpauth (~> 0.1) + jwt (~> 0.1.4) + multi_json (~> 1.0) + rack (~> 1.2) omniauth (1.1.0) hashie (~> 1.2) rack + omniauth-github (1.0.1) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) + omniauth-google-oauth2 (0.1.13) + omniauth (~> 1.0) + omniauth-oauth2 + omniauth-oauth (1.0.1) + oauth + omniauth (~> 1.0) + omniauth-oauth2 (1.1.0) + oauth2 (~> 0.8.0) + omniauth (~> 1.0) + omniauth-twitter (0.0.12) + multi_json (~> 1.3) + omniauth-oauth (~> 1.0) orm_adapter (0.3.0) polyglot (0.3.3) posix-spawn (0.3.6) @@ -411,7 +439,11 @@ DEPENDENCIES minitest (>= 2.10) modernizr (= 2.5.3) mysql2 + omniauth + omniauth-github + omniauth-google-oauth2 omniauth-ldap! + omniauth-twitter pry pygments.rb! rack-mini-profiler diff --git a/app/assets/stylesheets/auth_methods.scss b/app/assets/stylesheets/auth_methods.scss new file mode 100644 index 0000000000000..ed6f5b0fe2dc9 --- /dev/null +++ b/app/assets/stylesheets/auth_methods.scss @@ -0,0 +1,10 @@ +.auth_methods { + &ul { + margin: 0; + text-align:center; + padding: 5px; + &li { + display: inline; + } + } +} diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 5613f1e82d883..0e3de4e83fe7a 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -3,8 +3,8 @@ /** GITLAB colors **/ $text_color:#222; -$lite_text_color: #666; -$link_color:#2A79A3; +$lite_text_color: #666; +$link_color:#2A79A3; $active_link_color:#2FA0BB; $active_bg_color:#79C3E0; $active_bd_color: #2FA0BB; @@ -31,7 +31,7 @@ $hover: #FDF5D9; box-shadow: 0 0 3px #ddd; } -@mixin solid_shade { +@mixin solid_shade { -moz-box-shadow: 0 0 0 3px #eee; -webkit-box-shadow: 0 0 0 3px #eee; box-shadow: 0 0 0 3px #eee; @@ -73,21 +73,21 @@ $hover: #FDF5D9; /** - * Header of application. + * Header of application. * Contain application logo, search panel, profile icon */ @import "sections/header.scss"; /** - * Navigation menu of application. + * Navigation menu of application. * Panel with links to pages depends on project, profile or admin area */ @import "sections/nav.scss"; /** - * This file represent some UI that can be changed - * during web app restyle or theme select. - * + * This file represent some UI that can be changed + * during web app restyle or theme select. + * * Next items should be placed there * - link, button colors * - header restyles @@ -118,11 +118,11 @@ $hover: #FDF5D9; * Most of application styles placed here. * This file represent common UI that should not be changed between themes * or project restyling like form width or user avatar class or commit title - * + * * TODO: clean it */ @import "common.scss"; - +@import "auth_methods.scss"; /** * Styles related to specific part of app @@ -140,17 +140,17 @@ $hover: #FDF5D9; @import "ref_select.scss"; /** - * Code (files list) styles. Browsing project files there + * Code (files list) styles. Browsing project files there */ @import "sections/tree.scss"; /** - * This file represent notes(comments) styles + * This file represent notes(comments) styles */ @import "sections/notes.scss"; /** - * Devise styles + * Devise styles */ @import "sections/login.scss"; diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index d19931e93d77d..84e578a386509 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -9,7 +9,7 @@ def failure_message error ||= env["omniauth.error.type"].to_s error.to_s.humanize if error end - + def ldap # We only find ourselves here if the authentication to LDAP was successful. info = request.env["omniauth.auth"]["info"] @@ -20,4 +20,34 @@ def ldap sign_in_and_redirect @user end + Settings.omniauth_providers.each do |provider| + define_method provider['name'] do + handle_omniauth + end + end + + private + + def handle_omniauth + oauth = request.env['omniauth.auth'] + provider, uid = oauth['provider'], oauth['uid'] + + if current_user + # Change a logged-in user's authentication method: + current_user.uid = uid + current_user.provider = provider + current_user.save + redirect_to profile_path + else + @user = User.find_by_provider_and_uid(provider, uid) + + if @user + sign_in_and_redirect @user + else + flash[:notice] = "There's no such user!" + redirect_to new_user_session_path + end + end + end + end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 51569b0635eb0..8a457cea70fe3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -75,16 +75,16 @@ def app_theme end def show_last_push_widget?(event) - event && + event && event.last_push_to_non_root? && !event.rm_ref? && - event.project && + event.project && event.project.merge_requests_enabled end def tab_class(tab_key) active = case tab_key - + # Project Area when :wall; wall_tab? when :wiki; controller.controller_name == "wikis" @@ -123,4 +123,9 @@ def tab_class(tab_key) def hexdigest(string) Digest::SHA1.hexdigest string end + + def authbutton(provider, size = 64) + image_tag("authbuttons/#{provider.to_s.split('_').first}_#{size}.png", + alt: "Sign in with #{provider.to_s.titleize}" ) + end end diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index a03838669cf9c..6b334b8733560 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -14,10 +14,15 @@ <div class="right"> <%= render :partial => "devise/shared/links" %></div> <%- if devise_mapping.omniauthable? %> - <%- resource_class.omniauth_providers.each do |provider| %> - <hr/> - <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider), :class => "btn primary" %><br /> - <% end -%> + <hr/> + <div class="auth_methods"> + <ul> + <%- resource_class.omniauth_providers.each do |provider| %> + <li><%= link_to authbutton(provider), + omniauth_authorize_path(resource_name, provider) %></li> + <% end -%> + </ul> + </div> <% end -%> <% end %> diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index b624415dfe119..810b346f1cdf5 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -10,7 +10,7 @@ = link_to "Profile", profile_path %li{class: tab_class(:password)} - = link_to "Password", profile_password_path + = link_to "Authentication", profile_password_path %li{class: tab_class(:ssh_keys)} = link_to keys_path do diff --git a/app/views/profile/password.html.haml b/app/views/profile/password.html.haml index 257dacb1ad37b..1d4d468c38b0d 100644 --- a/app/views/profile/password.html.haml +++ b/app/views/profile/password.html.haml @@ -1,19 +1,31 @@ %h3.page_title Password %hr + = form_for @user, url: profile_password_path, method: :put do |f| - .data - %p.slead After successful password update you will be redirected to login page where you should login with new password - -if @user.errors.any? - .alert-message.block-message.error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg + .row + .span7 + .data + %p.slead After successful password update you will be redirected to login page where you should login with new password + -if @user.errors.any? + .alert-message.block-message.error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + + .clearfix + = f.label :password + .input= f.password_field :password + .clearfix + = f.label :password_confirmation + .input= f.password_field :password_confirmation - .clearfix - = f.label :password - .input= f.password_field :password - .clearfix - = f.label :password_confirmation - .input= f.password_field :password_confirmation + - if Settings.omniauth.enabled + .span5.right + .auth_methods.alert.alert-info + %strong Tip: Use one of the following sites to login + %ul + - User.omniauth_providers.each do |provider| + %li= link_to authbutton(provider), | + omniauth_authorize_path(User, provider) | .actions = f.submit 'Save', class: "btn primary" diff --git a/app/views/profile/show.html.haml b/app/views/profile/show.html.haml index 95cce2bb6b6c4..4d89cd3dcb333 100644 --- a/app/views/profile/show.html.haml +++ b/app/views/profile/show.html.haml @@ -49,6 +49,13 @@ %strong Tip: You can change your avatar at gravatar.com + - if Settings.omniauth.enabled && @user.provider? + %h4 + Omniauth Providers: + = link_to "Change", profile_password_path, class: "btn small right" + You can login through #{@user.provider.titleize}! + = authbutton(@user.provider, 32) + %h4 Personal projects: %small.right diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 1818f2c0d0101..622ac9ec560c3 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -1,4 +1,4 @@ -# # # # # # # # # # # # # # # # # # +# # # # # # # # # # # # # # # # # # # Gitlab application config file # # # # # # # # # # # # # # # # # # # @@ -19,14 +19,14 @@ email: # Application specific settings # Like default project limit for user etc -app: - default_projects_limit: 10 +app: + default_projects_limit: 10 # backup_path: "/vol/backups" # default: Rails.root + backups/ # backup_keep_time: 604800 # default: 0 (forever) (in seconds) -# -# 2. Advanced settings: +# +# 2. Advanced settings: # ========================== # Git Hosting configuration @@ -49,3 +49,15 @@ git: git_max_size: 5242880 # 5.megabytes # Git timeout to read commit, in seconds git_timeout: 10 + +# Omniauth configuration +# omniauth: +# enabled: true +# providers: +# - { name: 'google_oauth2', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET', +# args: { access_type: 'offline', approval_prompt: '' } } +# - { name: 'twitter', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET'} +# - { name: 'github', app_id: 'YOUR APP ID', +# app_secret: 'YOUR APP SECRET' } diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 5c5987a8857a9..741a29d51bebe 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -6,7 +6,7 @@ def web_protocol self.web['protocol'] ||= web.https ? "https" : "http" end - def web_host + def web_host self.web['host'] ||= 'localhost' end @@ -14,11 +14,11 @@ def email_from self.email['from'] ||= ("notify@" + web_host) end - def url + def url self['url'] ||= build_url - end + end - def web_port + def web_port if web.https web['port'] = 443 else @@ -36,7 +36,7 @@ def build_url raw_url << web_host if web_custom_port? - raw_url << ":#{web_port}" + raw_url << ":#{web_port}" end raw_url @@ -111,5 +111,14 @@ def backup_path def backup_keep_time app['backup_keep_time'] || 0 end + + def omniauth_enabled? + omniauth['enabled'] || false + end + + def omniauth_providers + omniauth['providers'] || [] + end + end end diff --git a/db/migrate/20120803152018_add_provider_and_uid_to_users.rb b/db/migrate/20120803152018_add_provider_and_uid_to_users.rb new file mode 100644 index 0000000000000..14f53e4ee231f --- /dev/null +++ b/db/migrate/20120803152018_add_provider_and_uid_to_users.rb @@ -0,0 +1,6 @@ +class AddProviderAndUidToUsers < ActiveRecord::Migration + def change + add_column :users, :provider, :string + add_column :users, :uid, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index c4c54f562a3ec..5ac159d8facdb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20120712080407) do +ActiveRecord::Schema.define(:version => 20120803152018) do create_table "events", :force => true do |t| t.string "target_type" @@ -146,31 +146,33 @@ end create_table "users", :force => true do |t| - t.string "email", :default => "", :null => false - t.string "encrypted_password", :limit => 128, :default => "", :null => false + t.string "email", :default => "", :null => false + t.string "encrypted_password", :default => "", :null => false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", :default => 0 + t.integer "sign_in_count", :default => 0 t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" t.string "last_sign_in_ip" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.string "name" - t.boolean "admin", :default => false, :null => false - t.integer "projects_limit", :default => 10 - t.string "skype", :default => "", :null => false - t.string "linkedin", :default => "", :null => false - t.string "twitter", :default => "", :null => false + t.boolean "admin", :default => false, :null => false + t.integer "projects_limit", :default => 10 + t.string "skype", :default => "", :null => false + t.string "linkedin", :default => "", :null => false + t.string "twitter", :default => "", :null => false t.string "authentication_token" - t.boolean "dark_scheme", :default => false, :null => false - t.integer "theme_id", :default => 1, :null => false + t.boolean "dark_scheme", :default => false, :null => false + t.integer "theme_id", :default => 1, :null => false t.string "bio" - t.boolean "blocked", :default => false, :null => false - t.integer "failed_attempts", :default => 0 + t.boolean "blocked", :default => false, :null => false + t.integer "failed_attempts", :default => 0 t.datetime "locked_at" + t.string "provider" + t.string "uid" end add_index "users", ["email"], :name => "index_users_on_email", :unique => true diff --git a/vendor/assets/images/authbuttons/github_32.png b/vendor/assets/images/authbuttons/github_32.png new file mode 100644 index 0000000000000000000000000000000000000000..247e52a5f4282110243afa350cd4a13b0154c2a1 GIT binary patch literal 1931 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 II;Vst0A0+q{r~^~ literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/github_64.png b/vendor/assets/images/authbuttons/github_64.png new file mode 100644 index 0000000000000000000000000000000000000000..fca7bf44652470a58edd12b410613656da95aed3 GIT binary patch literal 4447 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 literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/google_32.png b/vendor/assets/images/authbuttons/google_32.png new file mode 100644 index 0000000000000000000000000000000000000000..3909e9de93b22e1f3a2e6c264f6289331412a3bb GIT binary patch literal 1615 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 literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/google_64.png b/vendor/assets/images/authbuttons/google_64.png new file mode 100644 index 0000000000000000000000000000000000000000..e55f34f1b7d57c52172eb2bab0964037c532629b GIT binary patch literal 3446 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 literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/twitter_32.png b/vendor/assets/images/authbuttons/twitter_32.png new file mode 100644 index 0000000000000000000000000000000000000000..daadcffd315f11026cface9d99409997050ab9b9 GIT binary patch literal 1439 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANL}oCO|{#S9FrogmDZn=$tn z0|SF(iEBhjaDG}zd16s2gJVj5QmTSyZen_BP-<dIW#P$G8wLhe9Zwg>kcwMR!p~;Q zhKksKKeuQ7jPzM*rkUJo8y$TVG+ftQZS@d;<hRON(B;lA^^&KDrh1D%TJ|6)qjgb7 zScr&rNY2(H*Ft9R+qNZhmdQ)+GskDothRZ6r~3KX-!p!k@G3Y|TmJ0a@t<?6>z<eU zG5mYqe(UMpyD~Q>Ph5WA!qBE5;n7)x3(a2I5A0p0ES!>cu5F+Cbv0XY?RSxKpDOLP zPYb&ymZe_*@5kMxF$~7X?@L-5)IM2VwA3b8Ph645`NA!!uRnSXi?W26c?=dtUh6T< zx}gxO&eOI2<(+kkw$kxYGi<+KdNY&%eZ}m=WBcT!=GU#h6h3oh*zW}~Iu08>7;boP zZ|=C3{J2*#dqO+wBOap$6~zf0g>z1n`dokW_`!`Yw@voDE9bEbWSRW#Y85!Ol<CXG z2gUaymzo~D%;duJ*<JYCr45sfq91EKH@yGY@%%?VBaXtxchkL=9)6RvU3S8R7Y@uv z<(Z5mKFF{v;AWJuHDU7a;XCl>lhvHLds)@G=9X*hPBD>o<-Wno$$07am!3tx&&>Vx zXzkiH2RBSz_QK=&g%9matnTXW51zhk-ehKYMgQ~k>3NshdzF_y-tlQmQFC5|ftG6Y z$Ayi*BWLeoZ8$h>j-Z8A!^(@cy0=XX@Ax0sQXHImVO3aE@~L@K+;;`!JbBkJ%|dz0 zfq=W4!vixkd|&7=rkZFdEjKcAW#urAUn)@)vVfOiv#7!B(_5}Ry=yyFNoMvf{uwt! z9<%fOGGW=VaSz+`|9iL{Yy{f{HXEK|U$DiC_xH}1PM;;#h&+(^n6!Mc2-}CV`KI%8 zciz5migCI5qt5w)<x6*;54?6mz@%Jwcfy6Y`c+RiDP1mQuCiLEvnHrx)3J@({D<<s zDoqV#WY%jtn{4d$pse<C<6#k%U7xE<-gn1mtGE>Y(mnD{F+({qc(<_r=4GE%&Hf$y zUOd4hNqgsw5XZ0sGp;>$bLo=wj@_`8<-aYr%lF7A;mcR5!fS5roU}nGFSq+n)ng-J zg=>FRHWj_ytlWP7trthr4GaGti)TISb-VNF(!G6)CQNakRF!{lS?NLPvX#7<TJKz( z&K!x`v+?SiS4n%B54=8a!#4FFS7vr&(K?1zGbB|47KEOvX<jz9=ch`GPw3{`E0b8o zvrZTMIq4ofuevNJ!fpHG-8-!dKHa{bSX#<!@NL&6+i0uOhH{0CU*{Ya2-JDMZq4EA z#oV^bA8_}-Z@=E>b4%!*>)ONLWn@1n3YZx$)L{8o#xnn(5x;rZzju;HeiWDrSDiWA z;-}~|->=iaMs1t<Ht7!yWe<<6{9N!WX{|a#x?aq7$);sXm)^GIHd1*qed&#??`luC zC>hNE^Mk#n?xU5A+->&5SBsWCaeDRCRO#)`)8;EPJA-5o%xSve==9}fO0~tylqD}8 zxV3U~FT8c{M$=|*{w2G9W?g5=4?ch4cZ}}Et+}?GfkN(Ir?e!@4xDN-ar^1a>Yj_s zHwo)&AC6lxw|B+Hd23HMZtC^$(2BSh6T8R0<i$$yQh|40r(Q^DIXUBq*`j|@Ha>TH zGOOS1epdFif4kYgU4L%6zc+v7t$k%j$28CDZnX}L4pUyNZEWZF^Xs+$e`@vBg%?j2 z)qfRyd6d)e)R|gKcji#Ol{-|69)@L@Pn27>wR-Dn`?LQGeqFisHak3{cIuoPYV6W4 z!!)W}QdW3%hel~!S|^gB`=HeP^M!3WC)RBGGu799ip!#z?r)xT<hg&5@qQ&W`DBON z_YeCQ9+(vG6`5*nw;^_2)@tQ-PokY7{t7yrmHWQG;*atH230i;-@an6iwq1544$rj JF6*2Ung9o5ymbHo literal 0 HcmV?d00001 diff --git a/vendor/assets/images/authbuttons/twitter_64.png b/vendor/assets/images/authbuttons/twitter_64.png new file mode 100644 index 0000000000000000000000000000000000000000..68b74530c06b558b6c4848c2637d4caeaf4c969b GIT binary patch literal 3384 zcmeAS@N?(olHy`uVBq!ia0y~yU~m9o4mJh`hE<t`_ZS!$I14-?iy0WWg+Z8+Vb&Z8 z1_lPn64!{5;QX|b^2DN42FH~Aq*MjZ+{E<Mpwz^a%EFVWHVh0rM?GB}Ln>~)iH^;Y zbr-cW-u+!|+vFV^AG0|96A6lPnjEs$<rcfkuOL<}2iMxQEdoONHVRrF8y>I<*gs<Y z*w@k$(#cW5*u}L%D{$2!vn5)Au9sCT0xumo>;LwK-<uoztlNKD8W*R%-+OPbd6|P( z*sR_4n|BxQD}MIp+}Q_m_c#4!xWoRyj^X<kwW>GTRW;A*H!5apGc~AqW?qQ<H(z*t zN%W<)3)=oNCA@pO%k;bL?_T!f-)w%pHDz|5?|vjvk%dF^&S9sPbHC>OjoRV&JS~5o z;rEwQ@_xUry0YSyhogO2m1a_OdrOh^a%0O_9o>B<<*9#;umAswJ)MK+DnCQpV`lbw zzn>k{<+uHJ+va@xW}Rl6^A2f3OD6EKFthDF%H(k5=SRh5i%V)J2tJv|xT|ko!9I0` zi_I+uekFdI_raF&^5p0D|6b}8uP=$2fA<|*Q-s?acD|aA+5I^;zu(+%BGvoQO__af z`<{u7j7pmoIDWG!a~EDv;bnOFK*FK#vq0bN+0|i|T60TOY8S4pfByZ!m)?)U{(nE8 z(D!?3u2;gO7=O%a&D}2-4vO<Ump;p8GJ9v9uQuaOHM_}D3@;6MSfZV5WtuF~-JUOJ zW-|=)67&cR3fS4W`rMI*qX+MspZ~n?Ph_pXWv;=Vy5INSzgWEf^|e*p4GJIS&s^uR z$>lJ+V}8W^tKIojmWIy#qM6@?8x(>}H~QR}I-PB1$2u)v#_F>|k~>%1Tzn?$btAn+ zsj0QP{DSqb2u{|@;t!dNmM{MFj;�!!m)Qh)a96J48I%%fg|{=*M1hRXxK?VRqz; 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 V;mC$h*$fN}44$rjF6*2UngHt=Pv-yt literal 0 HcmV?d00001 -- GitLab