summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/helpers/internal_helpers.rb6
-rw-r--r--lib/api/internal.rb2
-rw-r--r--lib/api/issues.rb2
-rw-r--r--lib/api/users.rb37
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb2
-rw-r--r--lib/gitlab/usage_data.rb65
-rw-r--r--lib/gitlab/user_activities.rb34
8 files changed, 146 insertions, 9 deletions
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9919762cd82..64ab6f01eb5 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -18,6 +18,12 @@ module API
expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization
end
+ class UserActivity < Grape::Entity
+ expose :username
+ expose :last_activity_on
+ expose :last_activity_on, as: :last_activity_at # Back-compat
+ end
+
class Identity < Grape::Entity
expose :provider, :extern_uid
end
@@ -25,6 +31,7 @@ module API
class UserPublic < User
expose :last_sign_in_at
expose :confirmed_at
+ expose :last_activity_on
expose :email
expose :color_scheme_id, :projects_limit, :current_sign_in_at
expose :identities, using: Entities::Identity
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 810e5063996..718f936a1fc 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -60,6 +60,12 @@ module API
rescue JSON::ParserError
{}
end
+
+ def log_user_activity(actor)
+ commands = Gitlab::GitAccess::DOWNLOAD_COMMANDS
+
+ ::Users::ActivityService.new(actor, 'Git SSH').execute if commands.include?(params[:action])
+ end
end
end
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 215bc03d0e9..5b48ee8665f 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -40,6 +40,8 @@ module API
response = { status: access_status.status, message: access_status.message }
if access_status.status
+ log_user_activity(actor)
+
# Return the repository full path so that gitlab-shell has it when
# handling ssh commands
response[:repository_path] =
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 05423c17449..244725bb292 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -35,7 +35,7 @@ module API
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
optional :labels, type: String, desc: 'Comma-separated list of label names'
- optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
+ optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index eedc59f8636..46f221f68fe 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -39,10 +39,13 @@ module API
params do
# CE
optional :username, type: String, desc: 'Get a single user with a specific username'
+ optional :extern_uid, type: String, desc: 'Get a single user with a specific external authentication provider UID'
+ optional :provider, type: String, desc: 'The external provider'
optional :search, type: String, desc: 'Search for a username'
optional :active, type: Boolean, default: false, desc: 'Filters only active users'
optional :external, type: Boolean, default: false, desc: 'Filters only external users'
optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
+ all_or_none_of :extern_uid, :provider
use :pagination
end
@@ -51,14 +54,17 @@ module API
render_api_error!("Not authorized.", 403)
end
- if params[:username].present?
- users = User.where(username: params[:username])
- else
- users = User.all
- users = users.active if params[:active]
- users = users.search(params[:search]) if params[:search].present?
- users = users.blocked if params[:blocked]
- users = users.external if params[:external] && current_user.admin?
+ authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
+
+ users = User.all
+ users = User.where(username: params[:username]) if params[:username]
+ users = users.active if params[:active]
+ users = users.search(params[:search]) if params[:search].present?
+ users = users.blocked if params[:blocked]
+
+ if current_user.admin?
+ users = users.joins(:identities).merge(Identity.with_extern_uid(params[:provider], params[:extern_uid])) if params[:extern_uid] && params[:provider]
+ users = users.external if params[:external]
end
entity = current_user.admin? ? Entities::UserPublic : Entities::UserBasic
@@ -534,6 +540,21 @@ module API
email.destroy
current_user.update_secondary_emails!
end
+
+ desc 'Get a list of user activities'
+ params do
+ optional :from, type: DateTime, default: 6.months.ago, desc: 'Date string in the format YEAR-MONTH-DAY'
+ use :pagination
+ end
+ get "activities" do
+ authenticated_as_admin!
+
+ activities = User.
+ where(User.arel_table[:last_activity_on].gteq(params[:from])).
+ reorder(last_activity_on: :asc)
+
+ present paginate(activities), with: Entities::UserActivity
+ end
end
end
end
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 0b2b8bd7f4d..1a9d03beb51 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -9,6 +9,8 @@ module Banzai
VISIBLE_STATES = %w(closed merged).freeze
def call
+ return doc unless context[:issuable_state_filter_enabled]
+
extractor = Banzai::IssuableExtractor.new(project, current_user)
issuables = extractor.extract([doc])
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
new file mode 100644
index 00000000000..6aca6db3123
--- /dev/null
+++ b/lib/gitlab/usage_data.rb
@@ -0,0 +1,65 @@
+module Gitlab
+ class UsageData
+ include Gitlab::CurrentSettings
+
+ class << self
+ def data(force_refresh: false)
+ Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
+ end
+
+ def uncached_data
+ license_usage_data.merge(system_usage_data)
+ end
+
+ def to_json(force_refresh: false)
+ data(force_refresh: force_refresh).to_json
+ end
+
+ def system_usage_data
+ {
+ counts: {
+ boards: Board.count,
+ ci_builds: ::Ci::Build.count,
+ ci_pipelines: ::Ci::Pipeline.count,
+ ci_runners: ::Ci::Runner.count,
+ ci_triggers: ::Ci::Trigger.count,
+ deploy_keys: DeployKey.count,
+ deployments: Deployment.count,
+ environments: Environment.count,
+ groups: Group.count,
+ issues: Issue.count,
+ keys: Key.count,
+ labels: Label.count,
+ lfs_objects: LfsObject.count,
+ merge_requests: MergeRequest.count,
+ milestones: Milestone.count,
+ notes: Note.count,
+ pages_domains: PagesDomain.count,
+ projects: Project.count,
+ projects_prometheus_active: PrometheusService.active.count,
+ protected_branches: ProtectedBranch.count,
+ releases: Release.count,
+ services: Service.where(active: true).count,
+ snippets: Snippet.count,
+ todos: Todo.count,
+ uploads: Upload.count,
+ web_hooks: WebHook.count
+ }
+ }
+ end
+
+ def license_usage_data
+ usage_data = {
+ uuid: current_application_settings.uuid,
+ version: Gitlab::VERSION,
+ active_user_count: User.active.count,
+ recorded_at: Time.now,
+ mattermost_enabled: Gitlab.config.mattermost.enabled,
+ edition: 'CE'
+ }
+
+ usage_data
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/user_activities.rb b/lib/gitlab/user_activities.rb
new file mode 100644
index 00000000000..eb36ab9fded
--- /dev/null
+++ b/lib/gitlab/user_activities.rb
@@ -0,0 +1,34 @@
+module Gitlab
+ class UserActivities
+ include Enumerable
+
+ KEY = 'users:activities'.freeze
+ BATCH_SIZE = 500
+
+ def self.record(key, time = Time.now)
+ Gitlab::Redis.with do |redis|
+ redis.hset(KEY, key, time.to_i)
+ end
+ end
+
+ def delete(*keys)
+ Gitlab::Redis.with do |redis|
+ redis.hdel(KEY, keys)
+ end
+ end
+
+ def each
+ cursor = 0
+ loop do
+ cursor, pairs =
+ Gitlab::Redis.with do |redis|
+ redis.hscan(KEY, cursor, count: BATCH_SIZE)
+ end
+
+ Hash[pairs].each { |pair| yield pair }
+
+ break if cursor == '0'
+ end
+ end
+ end
+end