summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG8
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb8
-rw-r--r--app/controllers/profiles_controller.rb7
-rw-r--r--app/controllers/projects/services_controller.rb3
-rw-r--r--app/controllers/sessions_controller.rb7
-rw-r--r--app/models/audit_event.rb19
-rw-r--r--app/models/project_services/irker_service.rb109
-rw-r--r--app/models/security_event.rb2
-rw-r--r--app/services/audit_event_service.rb25
-rw-r--r--app/views/layouts/nav/_profile.html.haml6
-rw-r--r--app/views/profiles/_event_table.html.haml16
-rw-r--r--app/views/profiles/audit_log.html.haml5
-rw-r--r--app/views/profiles/history.html.haml11
-rw-r--r--app/workers/irker_worker.rb4
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20141118150935_add_audit_event.rb22
-rw-r--r--db/schema.rb18
-rw-r--r--doc/README.md2
-rw-r--r--doc/api/users.md28
-rw-r--r--doc/markdown/markdown.md30
-rw-r--r--doc/project_services/irker.md49
-rw-r--r--doc/release/monthly.md15
-rw-r--r--features/profile/active_tab.feature6
-rw-r--r--features/profile/profile.feature2
-rw-r--r--features/steps/profile/active_tab.rb4
-rw-r--r--features/steps/profile/profile.rb2
-rw-r--r--features/steps/shared/paths.rb4
-rw-r--r--lib/api/users.rb30
-rw-r--r--spec/features/security/profile_access_spec.rb4
-rw-r--r--spec/models/project_services/irker_service_spec.rb40
-rw-r--r--spec/requests/api/users_spec.rb51
-rw-r--r--spec/routing/routing_spec.rb4
34 files changed, 386 insertions, 161 deletions
diff --git a/CHANGELOG b/CHANGELOG
index ec28ffac92f..d538bb42992 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,11 @@
Please view this file on the master branch, on stable branches it's out of date.
v 7.13.0 (unreleased)
+ - Fix redirection to home page URL for unauthorized users (Daniel Gerhardt)
- Fix external issue tracker hook/test for HTTPS URLs (Daniel Gerhardt)
- Remove link leading to a 404 error in Deploy Keys page (Stan Hu)
- Add support for unlocking users in admin settings (Stan Hu)
+ - Add Irker service configuration options (Stan Hu)
- Fix order of issues imported form GitHub (Hiroyuki Sato)
- Bump rugments to 1.0.0beta8 to fix C prototype function highlighting (Jonathon Reinhart)
- Fix Merge Request webhook to properly fire "merge" action when accepted from the web UI
@@ -34,6 +36,12 @@ v 7.13.0 (unreleased)
- Faster automerge check and merge itself when source and target branches are in same repository
- Correctly show anonymous authorized applications under Profile > Applications.
- Query Optimization in MySQL.
+ - Allow users to be blocked and unblocked via the API
+
+v 7.12.2
+ - Correctly show anonymous authorized applications under Profile > Applications.
+ - Faster automerge check and merge itself when source and target branches are in same repository
+ - Audit log for user authentication
v 7.12.1
- Fix error when deleting a user who has projects (Stan Hu)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a9dcf67b1e2..69abadb151a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -67,7 +67,7 @@ To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gi
If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows:
-1. Fork the project on GitLab Cloud
+1. Fork the project into your personal space on GitLab.com
1. Create a feature branch
1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG)
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index a657d3c54ee..63fc146f1d1 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base
def authenticate_user!(*args)
# If user is not signed-in and tries to access root_path - redirect him to landing page
if current_application_settings.home_page_url.present?
- if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
+ if current_user.nil? && root_path == request.path
redirect_to current_application_settings.home_page_url and return
end
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 765adaf2128..fd51b380da2 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -28,6 +28,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Do additional LDAP checks for the user filter and EE features
if @user.allowed?
+ log_audit_event(gl_user, with: :ldap)
sign_in_and_redirect(gl_user)
else
flash[:alert] = "Access denied for your LDAP account."
@@ -47,6 +48,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
if current_user
# Add new authentication method
current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider'])
+ log_audit_event(current_user, with: oauth['provider'])
redirect_to profile_account_path, notice: 'Authentication method updated'
else
@user = Gitlab::OAuth::User.new(oauth)
@@ -54,6 +56,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login.
if @user.persisted? && @user.valid?
+ log_audit_event(@user.gl_user, with: oauth['provider'])
sign_in_and_redirect(@user.gl_user)
else
error_message =
@@ -83,4 +86,9 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def oauth
@oauth ||= request.env['omniauth.auth']
end
+
+ def log_audit_event(user, options = {})
+ AuditEventService.new(user, user, options).
+ for_authentication.security_event
+ end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 5382a6cf6ac..26a4de15462 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -38,8 +38,11 @@ class ProfilesController < Profiles::ApplicationController
redirect_to profile_account_path
end
- def history
- @events = current_user.recent_events.page(params[:page]).per(PER_PAGE)
+ def audit_log
+ @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
+ order("created_at DESC").
+ page(params[:page]).
+ per(PER_PAGE)
end
def update_username
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index dc18bbd8d5b..1e435be8275 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -7,7 +7,8 @@ class Projects::ServicesController < Projects::ApplicationController
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :send_from_committer_email, :disable_diffs, :external_wiki_url,
- :notify, :color]
+ :notify, :color,
+ :server_host, :server_port, :default_irc_uri]
# Authorize
before_action :authorize_admin_project!
before_action :service, only: [:edit, :update, :test]
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 7577fc96d6d..89629bc0581 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -37,6 +37,8 @@ class SessionsController < Devise::SessionsController
resource.update_attributes(reset_password_token: nil,
reset_password_sent_at: nil)
end
+ authenticated_with = user_params[:otp_attempt] ? "two-factor" : "standard"
+ log_audit_event(current_user, with: authenticated_with)
end
end
@@ -95,4 +97,9 @@ class SessionsController < Devise::SessionsController
user.valid_otp?(user_params[:otp_attempt]) ||
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
end
+
+ def log_audit_event(user, options = {})
+ AuditEventService.new(user, user, options).
+ for_authentication.security_event
+ end
end
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
new file mode 100644
index 00000000000..967ffd46db0
--- /dev/null
+++ b/app/models/audit_event.rb
@@ -0,0 +1,19 @@
+class AuditEvent < ActiveRecord::Base
+ serialize :details, Hash
+
+ belongs_to :user, foreign_key: :author_id
+
+ validates :author_id, presence: true
+ validates :entity_id, presence: true
+ validates :entity_type, presence: true
+
+ after_initialize :initialize_details
+
+ def initialize_details
+ self.details = {} if details.nil?
+ end
+
+ def author_name
+ self.user.name
+ end
+end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 89f312e8c98..a4d0914dbe7 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -21,26 +21,11 @@
require 'uri'
class IrkerService < Service
+ prop_accessor :server_host, :server_port, :default_irc_uri
prop_accessor :colorize_messages, :recipients, :channels
validates :recipients, presence: true, if: :activated?
- validate :check_recipients_count, if: :activated?
before_validation :get_channels
- after_initialize :initialize_settings
-
- # Writer for RSpec tests
- attr_writer :settings
-
- def initialize_settings
- # See the documentation (doc/project_services/irker.md) for possible values
- # here
- @settings ||= {
- server_ip: 'localhost',
- server_port: 6659,
- max_channels: 3,
- default_irc_uri: nil
- }
- end
def title
'Irker (IRC gateway)'
@@ -51,20 +36,6 @@ class IrkerService < Service
'gateway.'
end
- def help
- msg = 'Recipients have to be specified with a full URI: '\
- 'irc[s]://irc.network.net[:port]/#channel. Special cases: if you want '\
- 'the channel to be a nickname instead, append ",isnick" to the channel '\
- 'name; if the channel is protected by a secret password, append '\
- '"?key=secretpassword" to the URI.'
-
- unless @settings[:default_irc].nil?
- msg += ' Note that a default IRC URI is provided by this service\'s '\
- "administrator: #{default_irc}. You can thus just give a channel name."
- end
- msg
- end
-
def to_param
'irker'
end
@@ -77,30 +48,45 @@ class IrkerService < Service
return unless supported_events.include?(data[:object_kind])
IrkerWorker.perform_async(project_id, channels,
- colorize_messages, data, @settings)
+ colorize_messages, data, settings)
+ end
+
+ def settings
+ { server_host: server_host.present? ? server_host : 'localhost',
+ server_port: server_port.present? ? server_port : 6659
+ }
end
def fields
[
+ { type: 'text', name: 'server_host', placeholder: 'localhost',
+ help: 'Irker daemon hostname (defaults to localhost)' },
+ { type: 'text', name: 'server_port', placeholder: 6659,
+ help: 'Irker daemon port (defaults to 6659)' },
+ { type: 'text', name: 'default_irc_uri',
+ help: 'A default IRC URI to prepend before each recipient (optional)',
+ placeholder: 'irc://irc.network.net:6697/' },
{ type: 'textarea', name: 'recipients',
- placeholder: 'Recipients/channels separated by whitespaces' },
+ placeholder: 'Recipients/channels separated by whitespaces',
+ help: 'Recipients have to be specified with a full URI: '\
+ 'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
+ 'you want the channel to be a nickname instead, append ",isnick" to ' \
+ 'the channel name; if the channel is protected by a secret password, ' \
+ ' append "?key=secretpassword" to the URI. Note that if you specify a ' \
+ ' default IRC URI to prepend before each recipient, you can just give ' \
+ ' a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' },
]
end
- private
-
- def check_recipients_count
- return true if recipients.nil? || recipients.empty?
-
- if recipients.split(/\s+/).count > max_chans
- errors.add(:recipients, "are limited to #{max_chans}")
- end
+ def help
+ ' NOTE: Irker does NOT have built-in authentication, which makes it' \
+ ' vulnerable to spamming IRC channels if it is hosted outside of a ' \
+ ' firewall. Please make sure you run the daemon within a secured network ' \
+ ' to prevent abuse. For more details, read: http://www.catb.org/~esr/irker/security.html.'
end
- def max_chans
- @settings[:max_channels]
- end
+ private
def get_channels
return true unless :activated?
@@ -114,40 +100,35 @@ class IrkerService < Service
def map_recipients
self.channels = recipients.split(/\s+/).map do |recipient|
- format_channel default_irc_uri, recipient
+ format_channel(recipient)
end
channels.reject! &:nil?
end
- def default_irc_uri
- default_irc = @settings[:default_irc_uri]
- if !(default_irc.nil? || default_irc[-1] == '/')
- default_irc += '/'
- end
- default_irc
- end
-
- def format_channel(default_irc, recipient)
- cnt = 0
- url = nil
+ def format_channel(recipient)
+ uri = nil
# Try to parse the chan as a full URI
begin
- uri = URI.parse(recipient)
- raise URI::InvalidURIError if uri.scheme.nil? && cnt == 0
+ uri = consider_uri(URI.parse(recipient))
rescue URI::InvalidURIError
- unless default_irc.nil?
- cnt += 1
- recipient = "#{default_irc}#{recipient}"
- retry if cnt == 1
+ end
+
+ unless uri.present? and default_irc_uri.nil?
+ begin
+ new_recipient = URI.join(default_irc_uri, '/', recipient).to_s
+ uri = consider_uri(URI.parse(new_recipient))
+ rescue
+ Rails.logger.error("Unable to create a valid URL from #{default_irc_uri} and #{recipient}")
end
- else
- url = consider_uri uri
end
- url
+
+ uri
end
def consider_uri(uri)
+ return nil if uri.scheme.nil?
+
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
# Do not authorize irc://domain.com/
diff --git a/app/models/security_event.rb b/app/models/security_event.rb
new file mode 100644
index 00000000000..d131c11cb6c
--- /dev/null
+++ b/app/models/security_event.rb
@@ -0,0 +1,2 @@
+class SecurityEvent < AuditEvent
+end
diff --git a/app/services/audit_event_service.rb b/app/services/audit_event_service.rb
new file mode 100644
index 00000000000..a7f090655e1
--- /dev/null
+++ b/app/services/audit_event_service.rb
@@ -0,0 +1,25 @@
+class AuditEventService
+ def initialize(author, entity, details = {})
+ @author, @entity, @details = author, entity, details
+ end
+
+ def for_authentication
+ @details = {
+ with: @details[:with],
+ target_id: @author.id,
+ target_type: "User",
+ target_details: @author.name,
+ }
+
+ self
+ end
+
+ def security_event
+ SecurityEvent.create(
+ author_id: @author.id,
+ entity_id: @entity.id,
+ entity_type: @entity.class.name,
+ details: @details
+ )
+ end
+end
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index 914e1b83d1f..de5544268a1 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -44,8 +44,8 @@
= icon('image fw')
%span
Preferences
- = nav_link(path: 'profiles#history') do
- = link_to history_profile_path, title: 'History', data: {placement: 'right'} do
+ = nav_link(path: 'profiles#audit_log') do
+ = link_to audit_log_profile_path, title: 'Audit Log', data: {placement: 'right'} do
= icon('history fw')
%span
- History
+ Audit Log
diff --git a/app/views/profiles/_event_table.html.haml b/app/views/profiles/_event_table.html.haml
new file mode 100644
index 00000000000..c19ac429d52
--- /dev/null
+++ b/app/views/profiles/_event_table.html.haml
@@ -0,0 +1,16 @@
+%table.table#audits
+ %thead
+ %tr
+ %th Action
+ %th When
+
+ %tbody
+ - events.each do |event|
+ %tr
+ %td
+ %span
+ Signed in with
+ %b= event.details[:with]
+ authentication
+ %td #{time_ago_in_words event.created_at} ago
+= paginate events, theme: "gitlab"
diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml
new file mode 100644
index 00000000000..698d6037428
--- /dev/null
+++ b/app/views/profiles/audit_log.html.haml
@@ -0,0 +1,5 @@
+- page_title "Audit Log"
+%h3.page-title Audit Log
+%p.light History of authentications
+
+= render 'event_table', events: @events \ No newline at end of file
diff --git a/app/views/profiles/history.html.haml b/app/views/profiles/history.html.haml
deleted file mode 100644
index b414fb69f4e..00000000000
--- a/app/views/profiles/history.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-- page_title "History"
-%h3.page-title
- Your Account History
-%p.light
- All events created by your account are listed below.
-%hr
-.profile_history
- = render @events
-%hr
-= paginate @events, theme: "gitlab"
-
diff --git a/app/workers/irker_worker.rb b/app/workers/irker_worker.rb
index 84a54656df2..2d44d8d4dc6 100644
--- a/app/workers/irker_worker.rb
+++ b/app/workers/irker_worker.rb
@@ -19,7 +19,7 @@ class IrkerWorker
branch = "\x0305#{branch}\x0f"
end
- # Firsts messages are for branch creation/deletion
+ # First messages are for branch creation/deletion
send_branch_updates push_data, project, repo_name, committer, branch
# Next messages are for commits
@@ -34,7 +34,7 @@ class IrkerWorker
def init_perform(set, chans, colors)
@colors = colors
@channels = chans
- start_connection set['server_ip'], set['server_port']
+ start_connection set['server_host'], set['server_port']
end
def start_connection(irker_server, irker_port)
diff --git a/config/routes.rb b/config/routes.rb
index f904c975733..8617839a256 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -208,7 +208,7 @@ Gitlab::Application.routes.draw do
#
resource :profile, only: [:show, :update] do
member do
- get :history
+ get :audit_log
get :applications
put :reset_private_token
diff --git a/db/migrate/20141118150935_add_audit_event.rb b/db/migrate/20141118150935_add_audit_event.rb
new file mode 100644
index 00000000000..07383c6bbc7
--- /dev/null
+++ b/db/migrate/20141118150935_add_audit_event.rb
@@ -0,0 +1,22 @@
+class AddAuditEvent < ActiveRecord::Migration
+ def change
+ create_table :audit_events do |t|
+ t.integer :author_id, null: false
+ t.string :type, null: false
+
+ # "Namespace" where the change occurs
+ # eg. On a project, group or user
+ t.integer :entity_id, null: false
+ t.string :entity_type, null: false
+
+ # Details for the event
+ t.text :details
+
+ t.timestamps
+ end
+
+ add_index :audit_events, :author_id
+ add_index :audit_events, :type
+ add_index :audit_events, [:entity_id, :entity_type]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 3a5af6a76d4..8736d1e0df5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -28,16 +28,30 @@ ActiveRecord::Schema.define(version: 20150620233230) do
t.integer "default_branch_protection", default: 2
t.boolean "twitter_sharing_enabled", default: true
t.text "restricted_visibility_levels"
- t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
+ t.boolean "version_check_enabled", default: true
t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false
end
+ create_table "audit_events", force: true do |t|
+ t.integer "author_id", null: false
+ t.string "type", null: false
+ t.integer "entity_id", null: false
+ t.string "entity_type", null: false
+ t.text "details"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "audit_events", ["author_id"], name: "index_audit_events_on_author_id", using: :btree
+ add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
+ add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree
+
create_table "broadcast_messages", force: true do |t|
t.text "message", null: false
t.datetime "starts_at"
@@ -496,12 +510,12 @@ ActiveRecord::Schema.define(version: 20150620233230) do
t.string "bitbucket_access_token"
t.string "bitbucket_access_token_secret"
t.string "location"
+ t.string "public_email", default: "", null: false
t.string "encrypted_otp_secret"
t.string "encrypted_otp_secret_iv"
t.string "encrypted_otp_secret_salt"
t.boolean "otp_required_for_login", default: false, null: false
t.text "otp_backup_codes"
- t.string "public_email", default: "", null: false
t.integer "dashboard", default: 0
end
diff --git a/doc/README.md b/doc/README.md
index 424b9e9a47e..0524fda3ed6 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -4,7 +4,7 @@
- [API](api/README.md) Automate GitLab via a simple and powerful API.
- [GitLab as OAuth2 authentication service provider](integration/oauth_provider.md). It allows you to login to other applications from GitLab.
-- [GitLab Basics](gitlab_basics/README.md) Find step by step how to start working on your commandline and on GitLab.
+- [GitLab Basics](gitlab-basics/README.md) Find step by step how to start working on your commandline and on GitLab.
- [Importing to GitLab](workflow/importing/README.md).
- [Markdown](markdown/markdown.md) GitLab's advanced formatting system.
- [Permissions](permissions/permissions.md) Learn what each role in a project (guest/reporter/developer/master/owner) can do.
diff --git a/doc/api/users.md b/doc/api/users.md
index 8b04282f160..5dca77b5c7b 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -396,3 +396,31 @@ Parameters:
- `id` (required) - SSH key ID
Will return `200 OK` on success, or `404 Not found` if either user or key cannot be found.
+
+## Block user
+
+Blocks the specified user. Available only for admin.
+
+```
+PUT /users/:uid/block
+```
+
+Parameters:
+
+- `uid` (required) - id of specified user
+
+Will return `200 OK` on success, or `404 User Not Found` is user cannot be found.
+
+## Unblock user
+
+Unblocks the specified user. Available only for admin.
+
+```
+PUT /users/:uid/unblock
+```
+
+Parameters:
+
+- `uid` (required) - id of specified user
+
+Will return `200 OK` on success, or `404 User Not Found` is user cannot be found.
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 9c7f723c06d..322111ae9e1 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -343,6 +343,36 @@ Strikethrough uses two tildes. ~~Scratch this.~~
- Or minuses
+ Or pluses
+If a list item contains multiple paragraphs,
+each subsequent paragraph should be indented with four spaces.
+
+```no-highlight
+1. First ordered list item
+
+ Second paragraph of first item.
+2. Another item
+```
+
+1. First ordered list item
+
+ Second paragraph of first item.
+2. Another item
+
+If the second paragraph isn't indented with four spaces,
+the second list item will be incorrectly labeled as `1`.
+
+```no-highlight
+1. First ordered list item
+
+ Second paragraph of first item.
+2. Another item
+```
+
+1. First ordered list item
+
+ Second paragraph of first item.
+2. Another item
+
## Links
There are two ways to create links, inline-style and reference-style.
diff --git a/doc/project_services/irker.md b/doc/project_services/irker.md
index 9875bebf66b..1dbca20baf9 100644
--- a/doc/project_services/irker.md
+++ b/doc/project_services/irker.md
@@ -9,38 +9,43 @@ See the project homepage for further info: https://gitlab.com/esr/irker
## Needed setup
You will first need an Irker daemon. You can download the Irker code from its
-gitorious repository on https://gitorious.org/irker: `git clone
-git@gitorious.org:irker/irker.git`. Once you have downloaded the code, you can
-run the python script named `irkerd`. This script is the gateway script, it acts
-both as an IRC client, for sending messages to an IRC server obviously, and as a
-TCP server, for receiving messages from the GitLab service.
+repository on https://gitlab.com/esr/irker:
-If the Irker server runs on the same machine, you are done. If not, you will
-need to follow the firsts steps of the next section.
+```
+git clone https://gitlab.com/esr/irker.git
+```
-## Optional setup
+Once you have downloaded the code, you can run the python script named `irkerd`.
+This script is the gateway script, it acts both as an IRC client, for sending
+messages to an IRC server obviously, and as a TCP server, for receiving messages
+from the GitLab service.
-In the `app/models/project_services/irker_service.rb` file, you can modify some
-options in the `initialize_settings` method:
-- **server_ip** (defaults to `localhost`): the server IP address where the
-`irkerd` daemon runs;
-- **server_port** (defaults to `6659`): the server port of the `irkerd` daemon;
-- **max_channels** (defaults to `3`): the maximum number of recipients the
-client is authorized to join, per project;
-- **default_irc_uri** (no default) : if this option is set, it has to be in the
-format `irc[s]://domain.name` and will be prepend to each and every channel
-provided by the user which is not a full URI.
+If the Irker server runs on the same machine, you are done. If not, you will
+need to follow the firsts steps of the next section.
-If the Irker server and the GitLab application do not run on the same host, you
-will **need** to setup at least the **server_ip** option.
+## Complete these steps in GitLab:
+
+1. Navigate to the project you want to configure for notifications.
+1. Select "Settings" in the top navigation.
+1. Select "Services" in the left navigation.
+1. Click "Irker".
+1. Select the "Active" checkbox.
+1. Enter the server host address where `irkerd` runs (defaults to `localhost`)
+in the `Server host` field on the Web page
+1. Enter the server port of `irkerd` (e.g. defaults to 6659) in the
+`Server port` field on the Web page.
+1. Optional: if `Default irc uri` is set, it has to be in the format
+`irc[s]://domain.name` and will be prepend to each and every channel provided
+by the user which is not a full URI.
+1. Specify the recipients (e.g. #channel1, user1, etc.)
+1. Save or optionally click "Test Settings".
## Note on Irker recipients
Irker accepts channel names of the form `chan` and `#chan`, both for the
`#chan` channel. If you want to send messages in query, you will need to add
-`,isnick` avec the channel name, in this form: `Aorimn,isnick`. In this latter
+`,isnick` after the channel name, in this form: `Aorimn,isnick`. In this latter
case, `Aorimn` is treated as a nick and no more as a channel name.
Irker can also join password-protected channels. Users need to append
`?key=thesecretpassword` to the chan name.
-
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index 7fb22938690..3bc92187218 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -66,10 +66,11 @@ Xth: (2 working days before the 22nd)
Xth: (1 working day before the 22nd)
-- [ ] Create CE, EE, CI stable versions (#LINK)
-- [ ] Create Omnibus tags and build packages
-- [ ] Update GitLab.com with the stable version (#LINK)
-- [ ] Update ci.gitLab.com with the stable version (#LINK)
+- [ ] Merge CE stable into EE stable
+- [ ] Create (hopefully final) CE, EE, CI release candidates (#LINK)
+- [ ] Create Omnibus tags and build packages for the latest release candidates
+- [ ] Update GitLab.com with the latest RC (#LINK)
+- [ ] Update ci.gitLab.com with the latest RC (#LINK)
22nd before 12AM CET:
@@ -77,7 +78,11 @@ Release before 12AM CET / 3AM PST, to make sure the majority of our users
get the new version on the 22nd and there is sufficient time in the European
workday to quickly fix any issues.
-- [ ] Release CE, EE and CI (#LINK)
+- [ ] Merge CE stable into EE stable (#LINK)
+- [ ] Create the 'x.y.0' tag with the [release tools](https://dev.gitlab.org/gitlab/release-tools) (#LINK)
+- [ ] BEFORE 11AM CET Create and push omnibus tags for x.y.0 (will auto-release the packages) (#LINK)
+- [ ] BEFORE 12AM CET Publish the release blog post (#LINK)
+- [ ] Tweet about the release (blog post) (#LINK)
- [ ] Schedule a second tweet of the release announcement at 6PM CET / 9AM PST
```
diff --git a/features/profile/active_tab.feature b/features/profile/active_tab.feature
index 1fa4ac88ddc..788b7895d72 100644
--- a/features/profile/active_tab.feature
+++ b/features/profile/active_tab.feature
@@ -23,7 +23,7 @@ Feature: Profile Active Tab
Then the active main tab should be Preferences
And no other main tabs should be active
- Scenario: On Profile History
- Given I visit profile history page
- Then the active main tab should be History
+ Scenario: On Profile Audit Log
+ Given I visit Audit Log page
+ Then the active main tab should be Audit Log
And no other main tabs should be active
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
index 0dd0afde8b1..7a1345f2b37 100644
--- a/features/profile/profile.feature
+++ b/features/profile/profile.feature
@@ -63,7 +63,7 @@ Feature: Profile
Scenario: I visit history tab
Given I have activity
- When I visit profile history page
+ When I visit Audit Log page
Then I should see my activity
Scenario: I visit my user page
diff --git a/features/steps/profile/active_tab.rb b/features/steps/profile/active_tab.rb
index 79e3b55f6e1..4724a326277 100644
--- a/features/steps/profile/active_tab.rb
+++ b/features/steps/profile/active_tab.rb
@@ -19,7 +19,7 @@ class Spinach::Features::ProfileActiveTab < Spinach::FeatureSteps
ensure_active_main_tab('Preferences')
end
- step 'the active main tab should be History' do
- ensure_active_main_tab('History')
+ step 'the active main tab should be Audit Log' do
+ ensure_active_main_tab('Audit Log')
end
end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
index 11e1163c352..2b6b8b167f6 100644
--- a/features/steps/profile/profile.rb
+++ b/features/steps/profile/profile.rb
@@ -115,7 +115,7 @@ class Spinach::Features::Profile < Spinach::FeatureSteps
end
step 'I should see my activity' do
- expect(page).to have_content "#{current_user.name} closed issue"
+ expect(page).to have_content "Signed in with standard authentication"
end
step 'my password is expired' do
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 4cc01443c8b..fe651e81dac 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -127,8 +127,8 @@ module SharedPaths
visit profile_preferences_path
end
- step 'I visit profile history page' do
- visit history_profile_path
+ step 'I visit Audit Log page' do
+ visit audit_log_profile_path
end
# ----------------------------------------
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 9b268cfe8bc..c468371d3d4 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -199,6 +199,36 @@ module API
not_found!('User')
end
end
+
+ # Block user. Available only for admin
+ #
+ # Example Request:
+ # PUT /users/:id/block
+ put ':id/block' do
+ authenticated_as_admin!
+ user = User.find_by(id: params[:id])
+
+ if user
+ user.block
+ else
+ not_found!('User')
+ end
+ end
+
+ # Unblock user. Available only for admin
+ #
+ # Example Request:
+ # PUT /users/:id/unblock
+ put ':id/unblock' do
+ authenticated_as_admin!
+ user = User.find_by(id: params[:id])
+
+ if user
+ user.activate
+ else
+ not_found!('User')
+ end
+ end
end
resource :user do
diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb
index 8f7a9606262..bcabc2d53ac 100644
--- a/spec/features/security/profile_access_spec.rb
+++ b/spec/features/security/profile_access_spec.rb
@@ -45,8 +45,8 @@ describe "Profile access", feature: true do
it { is_expected.to be_denied_for :visitor }
end
- describe "GET /profile/history" do
- subject { history_profile_path }
+ describe "GET /profile/audit_log" do
+ subject { audit_log_profile_path }
it { is_expected.to be_allowed_for @u1 }
it { is_expected.to be_allowed_for :admin }
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index 37690434ec8..7d483a44c53 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -38,22 +38,6 @@ describe IrkerService do
let(:_recipients) { nil }
it { should validate_presence_of :recipients }
end
-
- context 'too many recipients' do
- let(:_recipients) { 'a b c d' }
- it 'should add an error if there is too many recipients' do
- subject.send :check_recipients_count
- expect(subject.errors).not_to be_blank
- end
- end
-
- context '3 recipients' do
- let(:_recipients) { 'a b c' }
- it 'should not add an error if there is 3 recipients' do
- subject.send :check_recipients_count
- expect(subject.errors).to be_blank
- end
- end
end
describe 'Execute' do
@@ -62,7 +46,7 @@ describe IrkerService do
let(:project) { create(:project) }
let(:sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
- let(:recipients) { '#commits' }
+ let(:recipients) { '#commits irc://test.net/#test ftp://bad' }
let(:colorize_messages) { '1' }
before do
@@ -71,17 +55,12 @@ describe IrkerService do
project: project,
project_id: project.id,
service_hook: true,
- properties: {
- 'recipients' => recipients,
- 'colorize_messages' => colorize_messages
- }
- )
- irker.settings = {
- server_ip: 'localhost',
+ server_host: 'localhost',
server_port: 6659,
- max_channels: 3,
- default_irc_uri: 'irc://chat.freenode.net/'
- }
+ default_irc_uri: 'irc://chat.freenode.net/',
+ recipients: recipients,
+ colorize_messages: colorize_messages)
+
irker.valid?
@irker_server = TCPServer.new 'localhost', 6659
end
@@ -97,11 +76,8 @@ describe IrkerService do
conn.readlines.each do |line|
msg = JSON.load(line.chomp("\n"))
expect(msg.keys).to match_array(['to', 'privmsg'])
- if msg['to'].is_a?(String)
- expect(msg['to']).to eq 'irc://chat.freenode.net/#commits'
- else
- expect(msg['to']).to match_array(['irc://chat.freenode.net/#commits'])
- end
+ expect(msg['to']).to match_array(["irc://chat.freenode.net/#commits",
+ "irc://test.net/#test"])
end
conn.close
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 1a29058f3f1..c4dd1f76cf2 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -527,4 +527,55 @@ describe API::API, api: true do
expect(response.status).to eq(401)
end
end
+
+ describe 'PUT /user/:id/block' do
+ before { admin }
+ it 'should block existing user' do
+ put api("/users/#{user.id}/block", admin)
+ expect(response.status).to eq(200)
+ expect(user.reload.state).to eq('blocked')
+ end
+
+ it 'should not be available for non admin users' do
+ put api("/users/#{user.id}/block", user)
+ expect(response.status).to eq(403)
+ expect(user.reload.state).to eq('active')
+ end
+
+ it 'should return a 404 error if user id not found' do
+ put api('/users/9999/block', admin)
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+ end
+
+ describe 'PUT /user/:id/unblock' do
+ before { admin }
+ it 'should unblock existing user' do
+ put api("/users/#{user.id}/unblock", admin)
+ expect(response.status).to eq(200)
+ expect(user.reload.state).to eq('active')
+ end
+
+ it 'should unblock a blocked user' do
+ put api("/users/#{user.id}/block", admin)
+ expect(response.status).to eq(200)
+ expect(user.reload.state).to eq('blocked')
+ put api("/users/#{user.id}/unblock", admin)
+ expect(response.status).to eq(200)
+ expect(user.reload.state).to eq('active')
+ end
+
+ it 'should not be available for non admin users' do
+ put api("/users/#{user.id}/unblock", user)
+ expect(response.status).to eq(403)
+ expect(user.reload.state).to eq('active')
+ end
+
+ it 'should return a 404 error if user id not found' do
+ put api('/users/9999/block', admin)
+ expect(response.status).to eq(404)
+ expect(json_response['message']).to eq('404 User Not Found')
+ end
+ end
end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 0fda6202a11..dd045826692 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -108,8 +108,8 @@ describe ProfilesController, "routing" do
expect(get("/profile/account")).to route_to('profiles/accounts#show')
end
- it "to #history" do
- expect(get("/profile/history")).to route_to('profiles#history')
+ it "to #audit_log" do
+ expect(get("/profile/audit_log")).to route_to('profiles#audit_log')
end
it "to #reset_private_token" do