diff options
59 files changed, 735 insertions, 340 deletions
@@ -51,7 +51,6 @@ gem 'omniauth-shibboleth', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.4' gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth-authentiq', '~> 0.3.1' -gem 'omniauth-jwt', '~> 0.0.2' gem 'rack-oauth2', '~> 1.2.1' gem 'jwt', '~> 1.5.6' diff --git a/Gemfile.lock b/Gemfile.lock index 54e3e73ecf4..31b0dc9c0e6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -554,9 +554,6 @@ GEM jwt (>= 1.5) omniauth (>= 1.1.1) omniauth-oauth2 (>= 1.5) - omniauth-jwt (0.0.2) - jwt - omniauth (~> 1.1) omniauth-kerberos (0.3.0) omniauth-multipassword timfel-krb5-auth (~> 0.8) @@ -1118,7 +1115,6 @@ DEPENDENCIES omniauth-github (~> 1.1.1) omniauth-gitlab (~> 1.0.2) omniauth-google-oauth2 (~> 0.5.3) - omniauth-jwt (~> 0.0.2) omniauth-kerberos (~> 0.3.0) omniauth-oauth2-generic (~> 0.2.2) omniauth-saml (~> 1.10) diff --git a/app/assets/javascripts/ide/stores/actions/file.js b/app/assets/javascripts/ide/stores/actions/file.js index 1a17320a1ea..66c60ad605a 100644 --- a/app/assets/javascripts/ide/stores/actions/file.js +++ b/app/assets/javascripts/ide/stores/actions/file.js @@ -60,7 +60,7 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive const file = state.entries[path]; commit(types.TOGGLE_LOADING, { entry: file }); return service - .getFileData(file.url) + .getFileData(`${gon.relative_url_root ? gon.relative_url_root : ''}${file.url}`) .then(res => { const pageTitle = decodeURI(normalizeHeaders(res.headers)['PAGE-TITLE']); setPageTitle(pageTitle); diff --git a/app/assets/javascripts/vue_shared/components/identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue index 0a30f467b08..23010f40f26 100644 --- a/app/assets/javascripts/vue_shared/components/identicon.vue +++ b/app/assets/javascripts/vue_shared/components/identicon.vue @@ -17,7 +17,7 @@ export default { }, computed: { /** - * This method is based on app/helpers/application_helper.rb#project_identicon + * This method is based on app/helpers/avatars_helper.rb#project_identicon */ identiconStyles() { const allowedColors = [ diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb index ebde0df1f7b..43d8867a536 100644 --- a/app/controllers/projects/lfs_storage_controller.rb +++ b/app/controllers/projects/lfs_storage_controller.rb @@ -77,8 +77,7 @@ class Projects::LfsStorageController < Projects::GitHttpClientController def link_to_project!(object) if object && !object.projects.exists?(storage_project.id) - object.projects << storage_project - object.save! + object.lfs_objects_projects.create!(project: storage_project) end end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 228c8d2e8f9..6aa307b4db4 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -32,80 +32,6 @@ module ApplicationHelper args.any? { |v| v.to_s.downcase == action_name } end - def project_icon(project_id, options = {}) - project = - if project_id.respond_to?(:avatar_url) - project_id - else - Project.find_by_full_path(project_id) - end - - if project.avatar_url - image_tag project.avatar_url, options - else # generated icon - project_identicon(project, options) - end - end - - def project_identicon(project, options = {}) - allowed_colors = { - red: 'FFEBEE', - purple: 'F3E5F5', - indigo: 'E8EAF6', - blue: 'E3F2FD', - teal: 'E0F2F1', - orange: 'FBE9E7', - gray: 'EEEEEE' - } - - options[:class] ||= '' - options[:class] << ' identicon' - bg_key = project.id % 7 - style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555" - - content_tag(:div, class: options[:class], style: style) do - project.name[0, 1].upcase - end - end - - # Takes both user and email and returns the avatar_icon by - # user (preferred) or email. - def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true) - if user - avatar_icon_for_user(user, size, scale, only_path: only_path) - elsif email - avatar_icon_for_email(email, size, scale, only_path: only_path) - else - default_avatar - end - end - - def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) - user = User.find_by_any_email(email.try(:downcase)) - if user - avatar_icon_for_user(user, size, scale, only_path: only_path) - else - gravatar_icon(email, size, scale) - end - end - - def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true) - if user - user.avatar_url(size: size, only_path: only_path) || default_avatar - else - gravatar_icon(nil, size, scale) - end - end - - def gravatar_icon(user_email = '', size = nil, scale = 2) - GravatarService.new.execute(user_email, size, scale) || - default_avatar - end - - def default_avatar - asset_path('no_avatar.png') - end - def last_commit(project) if project.repo_exists? time_ago_with_tooltip(project.repository.commit.committed_date) diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb index 21b6c0a8ad5..d339c01d492 100644 --- a/app/helpers/avatars_helper.rb +++ b/app/helpers/avatars_helper.rb @@ -1,4 +1,78 @@ module AvatarsHelper + def project_icon(project_id, options = {}) + project = + if project_id.respond_to?(:avatar_url) + project_id + else + Project.find_by_full_path(project_id) + end + + if project.avatar_url + image_tag project.avatar_url, options + else # generated icon + project_identicon(project, options) + end + end + + def project_identicon(project, options = {}) + allowed_colors = { + red: 'FFEBEE', + purple: 'F3E5F5', + indigo: 'E8EAF6', + blue: 'E3F2FD', + teal: 'E0F2F1', + orange: 'FBE9E7', + gray: 'EEEEEE' + } + + options[:class] ||= '' + options[:class] << ' identicon' + bg_key = project.id % 7 + style = "background-color: ##{allowed_colors.values[bg_key]}; color: #555" + + content_tag(:div, class: options[:class], style: style) do + project.name[0, 1].upcase + end + end + + # Takes both user and email and returns the avatar_icon by + # user (preferred) or email. + def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + elsif email + avatar_icon_for_email(email, size, scale, only_path: only_path) + else + default_avatar + end + end + + def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true) + user = User.find_by_any_email(email.try(:downcase)) + if user + avatar_icon_for_user(user, size, scale, only_path: only_path) + else + gravatar_icon(email, size, scale) + end + end + + def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true) + if user + user.avatar_url(size: size, only_path: only_path) || default_avatar + else + gravatar_icon(nil, size, scale) + end + end + + def gravatar_icon(user_email = '', size = nil, scale = 2) + GravatarService.new.execute(user_email, size, scale) || + default_avatar + end + + def default_avatar + ActionController::Base.helpers.image_path('no_avatar.png') + end + def author_avatar(commit_or_event, options = {}) user_avatar(options.merge({ user: commit_or_event.author, diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 866b8773db6..64b3145352d 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -17,7 +17,7 @@ module BlobHelper end def ide_edit_path(project = @project, ref = @ref, path = @path, options = {}) - "#{ide_path}/project#{edit_blob_path(project, ref, path, options)}" + "#{ide_path}/project#{url_for([project, "edit", "blob", id: [ref, path], script_name: "/"])}" end def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index e4212775956..3646e08a15f 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -16,6 +16,7 @@ class Notify < BaseMailer helper BlobHelper helper EmailsHelper helper MembersHelper + helper AvatarsHelper helper GitlabRoutingHelper def test_email(recipient_email, subject, body) diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 151a137debb..0a2e68af46c 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -11,7 +11,7 @@ module Ci before_save :set_size, if: :file_changed? - after_save :update_file_store + after_save :update_file_store, if: :file_changed? scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) } diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb index 7677891b9ce..13246a774e3 100644 --- a/app/models/concerns/avatarable.rb +++ b/app/models/concerns/avatarable.rb @@ -31,12 +31,13 @@ module Avatarable asset_host = ActionController::Base.asset_host use_asset_host = asset_host.present? + use_authentication = respond_to?(:public?) && !public? # Avatars for private and internal groups and projects require authentication to be viewed, # which means they can only be served by Rails, on the regular GitLab host. # If an asset host is configured, we need to return the fully qualified URL # instead of only the avatar path, so that Rails doesn't prefix it with the asset host. - if use_asset_host && respond_to?(:public?) && !public? + if use_asset_host && use_authentication use_asset_host = false only_path = false end @@ -49,6 +50,6 @@ module Avatarable url_base << gitlab_config.relative_url_root end - url_base + avatar.url + url_base + avatar.local_url end end diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index 454374121f3..94eef4ff7cd 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -31,7 +31,7 @@ module ProtectedRef end end - def protected_ref_accessible_to?(ref, user, action:, protected_refs: nil) + def protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil) access_levels_for_ref(ref, action: action, protected_refs: protected_refs).any? do |access_level| access_level.check_access(user) end diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 6b7f280fb70..84487031ee5 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -11,7 +11,7 @@ class LfsObject < ActiveRecord::Base mount_uploader :file, LfsObjectUploader - after_save :update_file_store + after_save :update_file_store, if: :file_changed? def update_file_store # The file.object_store is set during `uploader.store!` diff --git a/app/models/project.rb b/app/models/project.rb index 3f805dd1fc9..b64189ce7ee 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1041,13 +1041,6 @@ class Project < ActiveRecord::Base "#{web_url}.git" end - def user_can_push_to_empty_repo?(user) - return false unless empty_repo? - return false unless Ability.allowed?(user, :push_code, self) - - !ProtectedBranch.default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER - end - def forked? return true if fork_network && fork_network.root_project != self @@ -2000,10 +1993,11 @@ class Project < ActiveRecord::Base def fetch_branch_allows_maintainer_push?(user, branch_name) check_access = -> do + next false if empty_repo? + merge_request = source_of_merge_requests.opened .where(allow_maintainer_to_push: true) .find_by(source_branch: branch_name) - merge_request&.can_be_merged_by?(user) end diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 609780c5587..cb361a66591 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -4,6 +4,15 @@ class ProtectedBranch < ActiveRecord::Base protected_ref_access_levels :merge, :push + def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil) + # Masters, owners and admins are allowed to create the default branch + if default_branch_protected? && project.empty_repo? + return true if user.admin? || project.team.max_member_access(user.id) > Gitlab::Access::DEVELOPER + end + + super + end + # Check if branch name is marked as protected in the system def self.protected?(project, ref_name) return true if project.empty_repo? && default_branch_protected? diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index c9cb730c4e9..520710b757d 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -22,7 +22,7 @@ class GroupPolicy < BasePolicy condition(:can_change_parent_share_with_group_lock) { can?(:change_share_with_group_lock, @subject.parent) } condition(:has_projects) do - GroupProjectsFinder.new(group: @subject, current_user: @user).execute.any? + GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true }).execute.any? end with_options scope: :subject, score: 0 @@ -43,7 +43,11 @@ class GroupPolicy < BasePolicy end rule { admin } .enable :read_group - rule { has_projects } .enable :read_group + + rule { has_projects }.policy do + enable :read_group + enable :read_label + end rule { has_access }.enable :read_namespace diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index 484ac64580d..e8fe2bfe292 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -4,6 +4,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated include GitlabRoutingHelper include StorageHelper include TreeHelper + include ChecksCollaboration include Gitlab::Utils::StrongMemoize presents :project @@ -170,9 +171,11 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def can_current_user_push_to_branch?(branch) - return false unless repository.branch_exists?(branch) + user_access(project).can_push_to_branch?(branch) + end - ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch) + def can_current_user_push_to_default_branch? + can_current_user_push_to_branch?(default_branch) end def files_anchor_data @@ -200,7 +203,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def new_file_anchor_data - if current_user && can_current_user_push_code? + if current_user && can_current_user_push_to_default_branch? OpenStruct.new(enabled: false, label: _('New file'), link: project_new_blob_path(project, default_branch || 'master'), @@ -209,7 +212,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def readme_anchor_data - if current_user && can_current_user_push_code? && repository.readme.blank? + if current_user && can_current_user_push_to_default_branch? && repository.readme.blank? OpenStruct.new(enabled: false, label: _('Add Readme'), link: add_readme_path) @@ -221,7 +224,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def changelog_anchor_data - if current_user && can_current_user_push_code? && repository.changelog.blank? + if current_user && can_current_user_push_to_default_branch? && repository.changelog.blank? OpenStruct.new(enabled: false, label: _('Add Changelog'), link: add_changelog_path) @@ -233,7 +236,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def license_anchor_data - if current_user && can_current_user_push_code? && repository.license_blob.blank? + if current_user && can_current_user_push_to_default_branch? && repository.license_blob.blank? OpenStruct.new(enabled: false, label: _('Add License'), link: add_license_path) @@ -245,7 +248,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated end def contribution_guide_anchor_data - if current_user && can_current_user_push_code? && repository.contribution_guide.blank? + if current_user && can_current_user_push_to_default_branch? && repository.contribution_guide.blank? OpenStruct.new(enabled: false, label: _('Add Contribution guide'), link: add_contribution_guide_path) diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb index f12f0466a1d..f8a237178d9 100644 --- a/app/uploaders/gitlab_uploader.rb +++ b/app/uploaders/gitlab_uploader.rb @@ -65,6 +65,10 @@ class GitlabUploader < CarrierWave::Uploader::Base !!model end + def local_url + File.join('/', self.class.base_dir, dynamic_segment, filename) + end + private # Designed to be overridden by child uploaders that have a dynamic path diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index b15fe514a08..182cb01f250 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -58,7 +58,9 @@ touch README.md git add README.md git commit -m "add README" - git push -u origin master + - if @project.can_current_user_push_to_default_branch? + %span>< + git push -u origin master %fieldset %h5 Existing folder @@ -69,7 +71,9 @@ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} git add . git commit -m "Initial commit" - git push -u origin master + - if @project.can_current_user_push_to_default_branch? + %span>< + git push -u origin master %fieldset %h5 Existing Git repository @@ -78,8 +82,10 @@ cd existing_repo git remote rename origin old-origin git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')} - git push -u origin --all - git push -u origin --tags + - if @project.can_current_user_push_to_default_branch? + %span>< + git push -u origin --all + git push -u origin --tags - if can? current_user, :remove_project, @project .prepend-top-20 diff --git a/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml b/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml new file mode 100644 index 00000000000..80b5b4a8abe --- /dev/null +++ b/changelogs/unreleased/44775-avatar-on-os-fails-with-cdn.yml @@ -0,0 +1,5 @@ +--- +title: Fixed wrong avatar URL when the avatar is on object storage. +merge_request: 18092 +author: +type: fixed diff --git a/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml b/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml new file mode 100644 index 00000000000..22a839cef56 --- /dev/null +++ b/changelogs/unreleased/add-jwt-strategy-to-gitlab-suite.yml @@ -0,0 +1,5 @@ +--- +title: Ports omniauth-jwt gem onto GitLab OmniAuth Strategies suite +merge_request: 18580 +author: +type: fixed diff --git a/changelogs/unreleased/bvl-fix-maintainer-push-error.yml b/changelogs/unreleased/bvl-fix-maintainer-push-error.yml new file mode 100644 index 00000000000..66ab8fbf884 --- /dev/null +++ b/changelogs/unreleased/bvl-fix-maintainer-push-error.yml @@ -0,0 +1,5 @@ +--- +title: Fix errors on pushing to an empty repository +merge_request: 18462 +author: +type: fixed diff --git a/changelogs/unreleased/bvl-fix-openid-redirect.yml b/changelogs/unreleased/bvl-fix-openid-redirect.yml new file mode 100644 index 00000000000..83ee6d953e4 --- /dev/null +++ b/changelogs/unreleased/bvl-fix-openid-redirect.yml @@ -0,0 +1,5 @@ +--- +title: Fix redirection error for applications using OpenID +merge_request: 18599 +author: +type: fixed diff --git a/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml b/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml new file mode 100644 index 00000000000..9f057c67122 --- /dev/null +++ b/changelogs/unreleased/dm-commit-trailer-without-gravatar.yml @@ -0,0 +1,5 @@ +--- +title: Fix commit trailer rendering when Gravatar is disabled +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/fix-file-store-artifacts-and-lfs.yml b/changelogs/unreleased/fix-file-store-artifacts-and-lfs.yml new file mode 100644 index 00000000000..7e97f245e66 --- /dev/null +++ b/changelogs/unreleased/fix-file-store-artifacts-and-lfs.yml @@ -0,0 +1,5 @@ +--- +title: Fix file_store for artifacts and lfs when saving +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/issue_45463.yml b/changelogs/unreleased/issue_45463.yml new file mode 100644 index 00000000000..a350568d04b --- /dev/null +++ b/changelogs/unreleased/issue_45463.yml @@ -0,0 +1,5 @@ +--- +title: Fix users not seeing labels from private groups when being a member of a child project +merge_request: +author: +type: fixed diff --git a/changelogs/unreleased/update-doorkeeper-changelog.yml b/changelogs/unreleased/update-doorkeeper-changelog.yml new file mode 100644 index 00000000000..b47bdf4a28d --- /dev/null +++ b/changelogs/unreleased/update-doorkeeper-changelog.yml @@ -0,0 +1,5 @@ +--- +title: Update doorkeeper to 4.3.2 to fix GitLab OAuth authentication +merge_request: 18543 +author: +type: fixed diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 8c39a1f2aa9..722ac67da0b 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -532,7 +532,7 @@ production: &base # required_claims: ["name", "email"], # info_map: { name: "name", email: "email" }, # auth_url: 'https://example.com/', - # valid_within: nil, + # valid_within: null, # } # } # - { name: 'saml', @@ -823,7 +823,7 @@ test: required_claims: ["name", "email"], info_map: { name: "name", email: "email" }, auth_url: 'https://example.com/', - valid_within: nil, + valid_within: null, } } - { name: 'auth0', diff --git a/config/initializers/fast_gettext.rb b/config/initializers/9_fast_gettext.rb index fd0167aa476..fd0167aa476 100644 --- a/config/initializers/fast_gettext.rb +++ b/config/initializers/9_fast_gettext.rb diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 2079d3acb72..e3a342590d4 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -104,5 +104,5 @@ Doorkeeper.configure do # set to true if you want this to be allowed # wildcard_redirect_uri false - base_controller 'ApplicationController' + base_controller '::Gitlab::BaseDoorkeeperController' end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 00baea08613..e33ebb25c4c 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -25,5 +25,6 @@ end module OmniAuth module Strategies autoload :Bitbucket, Rails.root.join('lib', 'omni_auth', 'strategies', 'bitbucket') + autoload :Jwt, Rails.root.join('lib', 'omni_auth', 'strategies', 'jwt') end end diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md index b51e705ab52..8b00f52ffc1 100644 --- a/doc/administration/auth/jwt.md +++ b/doc/administration/auth/jwt.md @@ -50,7 +50,7 @@ JWT will provide you with a secret key for you to use. required_claims: ["name", "email"], info_map: { name: "name", email: "email" }, auth_url: 'https://example.com/', - valid_within: nil, + valid_within: null, } } ``` diff --git a/lib/banzai/filter/commit_trailers_filter.rb b/lib/banzai/filter/commit_trailers_filter.rb index ef16df1f3ae..7b55e8b36f6 100644 --- a/lib/banzai/filter/commit_trailers_filter.rb +++ b/lib/banzai/filter/commit_trailers_filter.rb @@ -13,7 +13,6 @@ module Banzai # * https://git.wiki.kernel.org/index.php/CommitMessageConventions class CommitTrailersFilter < HTML::Pipeline::Filter include ActionView::Helpers::TagHelper - include ApplicationHelper include AvatarsHelper TRAILER_REGEXP = /(?<label>[[:alpha:]-]+-by:)/i.freeze diff --git a/lib/gitlab/base_doorkeeper_controller.rb b/lib/gitlab/base_doorkeeper_controller.rb new file mode 100644 index 00000000000..e4227af25d2 --- /dev/null +++ b/lib/gitlab/base_doorkeeper_controller.rb @@ -0,0 +1,8 @@ +# This is a base controller for doorkeeper. +# It adds the `can?` helper used in the views. +module Gitlab + class BaseDoorkeeperController < ActionController::Base + include Gitlab::Allowable + helper_method :can? + end +end diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb index 69952cbb47c..8cf5d636743 100644 --- a/lib/gitlab/user_access.rb +++ b/lib/gitlab/user_access.rb @@ -63,10 +63,12 @@ module Gitlab request_cache def can_push_to_branch?(ref) return false unless can_access_git? - return false unless user.can?(:push_code, project) || project.branch_allows_maintainer_push?(user, ref) + return false unless project + + return false if !user.can?(:push_code, project) && !project.branch_allows_maintainer_push?(user, ref) if protected?(ProtectedBranch, project, ref) - project.user_can_push_to_empty_repo?(user) || protected_branch_accessible_to?(ref, action: :push) + protected_branch_accessible_to?(ref, action: :push) else true end @@ -101,6 +103,7 @@ module Gitlab def protected_branch_accessible_to?(ref, action:) ProtectedBranch.protected_ref_accessible_to?( ref, user, + project: project, action: action, protected_refs: project.protected_branches) end @@ -108,6 +111,7 @@ module Gitlab def protected_tag_accessible_to?(ref, action:) ProtectedTag.protected_ref_accessible_to?( ref, user, + project: project, action: action, protected_refs: project.protected_tags) end diff --git a/lib/omni_auth/strategies/jwt.rb b/lib/omni_auth/strategies/jwt.rb new file mode 100644 index 00000000000..2349b2a28aa --- /dev/null +++ b/lib/omni_auth/strategies/jwt.rb @@ -0,0 +1,62 @@ +require 'omniauth' +require 'jwt' + +module OmniAuth + module Strategies + class JWT + ClaimInvalid = Class.new(StandardError) + + include OmniAuth::Strategy + + args [:secret] + + option :secret, nil + option :algorithm, 'HS256' + option :uid_claim, 'email' + option :required_claims, %w(name email) + option :info_map, { name: "name", email: "email" } + option :auth_url, nil + option :valid_within, nil + + uid { decoded[options.uid_claim] } + + extra do + { raw_info: decoded } + end + + info do + options.info_map.each_with_object({}) do |(k, v), h| + h[k.to_s] = decoded[v.to_s] + end + end + + def request_phase + redirect options.auth_url + end + + def decoded + @decoded ||= ::JWT.decode(request.params['jwt'], options.secret, options.algorithm).first + + (options.required_claims || []).each do |field| + raise ClaimInvalid, "Missing required '#{field}' claim" unless @decoded.key?(field.to_s) + end + + raise ClaimInvalid, "Missing required 'iat' claim" if options.valid_within && !@decoded["iat"] + + if options.valid_within && (Time.now.to_i - @decoded["iat"]).abs > options.valid_within + raise ClaimInvalid, "'iat' timestamp claim is too skewed from present" + end + + @decoded + end + + def callback_phase + super + rescue ClaimInvalid => e + fail! :claim_invalid, e + end + end + + class Jwt < JWT; end + end +end diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb index 08e2ccf893a..c3468536ae1 100644 --- a/spec/controllers/projects/raw_controller_spec.rb +++ b/spec/controllers/projects/raw_controller_spec.rb @@ -54,9 +54,9 @@ describe Projects::RawController do end context 'and lfs uses object storage' do + let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') } + before do - lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") - lfs_object.save! stub_lfs_object_storage lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE) end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 1761b6e2a3b..da7255d8ad9 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -151,6 +151,13 @@ FactoryBot.define do end end + trait :stubbed_repository do + after(:build) do |project| + allow(project).to receive(:empty_repo?).and_return(false) + allow(project.repository).to receive(:empty?).and_return(false) + end + end + trait :wiki_repo do after(:create) do |project| raise 'Failed to create wiki repository!' unless project.create_wiki diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb index 3e05e7b7f38..ae41f611ddc 100644 --- a/spec/features/labels_hierarchy_spec.rb +++ b/spec/features/labels_hierarchy_spec.rb @@ -170,6 +170,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do context 'on issue sidebar' do before do + project_1.add_developer(user) + visit project_issue_path(project_1, issue) end @@ -180,6 +182,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, project: project_1) } before do + project_1.add_developer(user) + visit project_board_path(project_1, board) wait_for_requests @@ -194,6 +198,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, group: parent) } before do + parent.add_developer(user) + visit group_board_path(parent, board) wait_for_requests @@ -211,6 +217,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do context 'on project issuable list' do before do + project_1.add_developer(user) + visit project_issues_path(project_1) end @@ -237,6 +245,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, project: project_1) } before do + project_1.add_developer(user) + visit project_board_path(project_1, board) end @@ -247,6 +257,8 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, group: parent) } before do + parent.add_developer(user) + visit group_board_path(parent, board) end @@ -259,6 +271,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, project: project_1) } before do + project_1.add_developer(user) visit project_board_path(project_1, board) find('.js-new-board-list').click wait_for_requests @@ -281,6 +294,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do let(:board) { create(:board, group: parent) } before do + parent.add_developer(user) visit group_board_path(parent, board) find('.js-new-board-list').click wait_for_requests diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb new file mode 100644 index 00000000000..b7d063596c1 --- /dev/null +++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +feature 'User creates blob in new project', :js do + let(:user) { create(:user) } + let(:project) { create(:project, :empty_repo) } + + shared_examples 'creating a file' do + before do + sign_in(user) + visit project_path(project) + end + + it 'allows the user to add a new file' do + click_link 'New file' + + find('#editor') + execute_script('ace.edit("editor").setValue("Hello world")') + + fill_in(:file_name, with: 'dummy-file') + + click_button('Commit changes') + + expect(page).to have_content('The file has been successfully created') + end + end + + describe 'as a master' do + before do + project.add_master(user) + end + + it_behaves_like 'creating a file' + end + + describe 'as an admin' do + let(:user) { create(:user, :admin) } + + it_behaves_like 'creating a file' + end + + describe 'as a developer' do + before do + project.add_developer(user) + sign_in(user) + visit project_path(project) + end + + it 'does not allow pushing to the default branch' do + expect(page).not_to have_content('New file') + end + end +end diff --git a/spec/features/projects/user_views_empty_project_spec.rb b/spec/features/projects/user_views_empty_project_spec.rb new file mode 100644 index 00000000000..7b982301ffc --- /dev/null +++ b/spec/features/projects/user_views_empty_project_spec.rb @@ -0,0 +1,43 @@ +require 'spec_helper' + +describe 'User views an empty project' do + let(:project) { create(:project, :empty_repo) } + let(:user) { create(:user) } + + shared_examples 'allowing push to default branch' do + before do + sign_in(user) + visit project_path(project) + end + + it 'shows push-to-master instructions' do + expect(page).to have_content('git push -u origin master') + end + end + + describe 'as a master' do + before do + project.add_master(user) + end + + it_behaves_like 'allowing push to default branch' + end + + describe 'as an admin' do + let(:user) { create(:user, :admin) } + + it_behaves_like 'allowing push to default branch' + end + + describe 'as a developer' do + before do + project.add_developer(user) + sign_in(user) + visit project_path(project) + end + + it 'does not show push-to-master instructions' do + expect(page).not_to have_content('git push -u origin master') + end + end +end diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 43cb0dfe163..5e454f8b310 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -2,8 +2,6 @@ require 'spec_helper' describe ApplicationHelper do - include UploadHelpers - describe 'current_controller?' do it 'returns true when controller matches argument' do stub_controller_name('foo') @@ -54,143 +52,6 @@ describe ApplicationHelper do end end - describe 'project_icon' do - it 'returns an url for the avatar' do - project = create(:project, :public, avatar: File.open(uploaded_image_temp_path)) - - expect(helper.project_icon(project.full_path).to_s) - .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />" - end - end - - describe 'avatar_icon_for' do - let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') } - let(:email) { 'foo@example.com' } - let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) } - - it 'prefers the user to retrieve the avatar_url' do - expect(helper.avatar_icon_for(user, email).to_s) - .to eq(user.avatar.url) - end - - it 'falls back to email lookup if no user given' do - expect(helper.avatar_icon_for(nil, email).to_s) - .to eq(another_user.avatar.url) - end - end - - describe 'avatar_icon_for_email' do - let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) } - - context 'using an email' do - context 'when there is a matching user' do - it 'returns a relative URL for the avatar' do - expect(helper.avatar_icon_for_email(user.email).to_s) - .to eq(user.avatar.url) - end - end - - context 'when no user exists for the email' do - it 'calls gravatar_icon' do - expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) - - helper.avatar_icon_for_email('foo@example.com', 20, 2) - end - end - - context 'without an email passed' do - it 'calls gravatar_icon' do - expect(helper).to receive(:gravatar_icon).with(nil, 20, 2) - - helper.avatar_icon_for_email(nil, 20, 2) - end - end - end - end - - describe 'avatar_icon_for_user' do - let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) } - - context 'with a user object passed' do - it 'returns a relative URL for the avatar' do - expect(helper.avatar_icon_for_user(user).to_s) - .to eq(user.avatar.url) - end - end - - context 'without a user object passed' do - it 'calls gravatar_icon' do - expect(helper).to receive(:gravatar_icon).with(nil, 20, 2) - - helper.avatar_icon_for_user(nil, 20, 2) - end - end - end - - describe 'gravatar_icon' do - let(:user_email) { 'user@email.com' } - - context 'with Gravatar disabled' do - before do - stub_application_setting(gravatar_enabled?: false) - end - - it 'returns a generic avatar' do - expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png') - end - end - - context 'with Gravatar enabled' do - before do - stub_application_setting(gravatar_enabled?: true) - end - - it 'returns a generic avatar when email is blank' do - expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png') - end - - it 'returns a valid Gravatar URL' do - stub_config_setting(https: false) - - expect(helper.gravatar_icon(user_email)) - .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118') - end - - it 'uses HTTPs when configured' do - stub_config_setting(https: true) - - expect(helper.gravatar_icon(user_email)) - .to match('https://secure.gravatar.com') - end - - it 'returns custom gravatar path when gravatar_url is set' do - stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}') - - expect(gravatar_icon(user_email, 20)) - .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118') - end - - it 'accepts a custom size argument' do - expect(helper.gravatar_icon(user_email, 64)).to include '?s=128' - end - - it 'defaults size to 40@2x when given an invalid size' do - expect(helper.gravatar_icon(user_email, nil)).to include '?s=80' - end - - it 'accepts a scaling factor' do - expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120' - end - - it 'ignores case and surrounding whitespace' do - normal = helper.gravatar_icon('foo@example.com') - upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ') - - expect(normal).to eq upcase - end - end - end - describe 'simple_sanitize' do let(:a_tag) { '<a href="#">Foo</a>' } diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb index 04c6d259135..5856bccb5b8 100644 --- a/spec/helpers/avatars_helper_spec.rb +++ b/spec/helpers/avatars_helper_spec.rb @@ -1,10 +1,147 @@ require 'rails_helper' describe AvatarsHelper do - include ApplicationHelper + include UploadHelpers let(:user) { create(:user) } + describe '#project_icon' do + it 'returns an url for the avatar' do + project = create(:project, :public, avatar: File.open(uploaded_image_temp_path)) + + expect(helper.project_icon(project.full_path).to_s) + .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />" + end + end + + describe '#avatar_icon_for' do + let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') } + let(:email) { 'foo@example.com' } + let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) } + + it 'prefers the user to retrieve the avatar_url' do + expect(helper.avatar_icon_for(user, email).to_s) + .to eq(user.avatar.url) + end + + it 'falls back to email lookup if no user given' do + expect(helper.avatar_icon_for(nil, email).to_s) + .to eq(another_user.avatar.url) + end + end + + describe '#avatar_icon_for_email' do + let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) } + + context 'using an email' do + context 'when there is a matching user' do + it 'returns a relative URL for the avatar' do + expect(helper.avatar_icon_for_email(user.email).to_s) + .to eq(user.avatar.url) + end + end + + context 'when no user exists for the email' do + it 'calls gravatar_icon' do + expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2) + + helper.avatar_icon_for_email('foo@example.com', 20, 2) + end + end + + context 'without an email passed' do + it 'calls gravatar_icon' do + expect(helper).to receive(:gravatar_icon).with(nil, 20, 2) + + helper.avatar_icon_for_email(nil, 20, 2) + end + end + end + end + + describe '#avatar_icon_for_user' do + let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) } + + context 'with a user object passed' do + it 'returns a relative URL for the avatar' do + expect(helper.avatar_icon_for_user(user).to_s) + .to eq(user.avatar.url) + end + end + + context 'without a user object passed' do + it 'calls gravatar_icon' do + expect(helper).to receive(:gravatar_icon).with(nil, 20, 2) + + helper.avatar_icon_for_user(nil, 20, 2) + end + end + end + + describe '#gravatar_icon' do + let(:user_email) { 'user@email.com' } + + context 'with Gravatar disabled' do + before do + stub_application_setting(gravatar_enabled?: false) + end + + it 'returns a generic avatar' do + expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png') + end + end + + context 'with Gravatar enabled' do + before do + stub_application_setting(gravatar_enabled?: true) + end + + it 'returns a generic avatar when email is blank' do + expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png') + end + + it 'returns a valid Gravatar URL' do + stub_config_setting(https: false) + + expect(helper.gravatar_icon(user_email)) + .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118') + end + + it 'uses HTTPs when configured' do + stub_config_setting(https: true) + + expect(helper.gravatar_icon(user_email)) + .to match('https://secure.gravatar.com') + end + + it 'returns custom gravatar path when gravatar_url is set' do + stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}') + + expect(gravatar_icon(user_email, 20)) + .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118') + end + + it 'accepts a custom size argument' do + expect(helper.gravatar_icon(user_email, 64)).to include '?s=128' + end + + it 'defaults size to 40@2x when given an invalid size' do + expect(helper.gravatar_icon(user_email, nil)).to include '?s=80' + end + + it 'accepts a scaling factor' do + expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120' + end + + it 'ignores case and surrounding whitespace' do + normal = helper.gravatar_icon('foo@example.com') + upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ') + + expect(normal).to eq upcase + end + end + end + describe '#user_avatar' do subject { helper.user_avatar(user: user) } diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 1fa194fe1b8..8de615ad8c2 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -242,4 +242,29 @@ describe BlobHelper do end end end + + describe '#ide_edit_path' do + let(:project) { create(:project) } + + around do |example| + old_script_name = Rails.application.routes.default_url_options[:script_name] + begin + example.run + ensure + Rails.application.routes.default_url_options[:script_name] = old_script_name + end + end + + it 'returns full IDE path' do + Rails.application.routes.default_url_options[:script_name] = nil + + expect(helper.ide_edit_path(project, "master", "")).to eq("/-/ide/project/#{project.namespace.path}/#{project.path}/edit/master/") + end + + it 'returns IDE path without relative_url_root' do + Rails.application.routes.default_url_options[:script_name] = "/gitlab" + + expect(helper.ide_edit_path(project, "master", "")).to eq("/gitlab/-/ide/project/#{project.namespace.path}/#{project.path}/edit/master/") + end + end end diff --git a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb index 1fd145116df..068cdc85a07 100644 --- a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb +++ b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb @@ -47,16 +47,36 @@ describe Banzai::Filter::CommitTrailersFilter do ) end - it 'non GitLab users and replaces them with mailto links' do - _, message_html = build_commit_message( - trailer: trailer, - name: FFaker::Name.name, - email: email - ) + context 'non GitLab users' do + shared_examples 'mailto links' do + it 'replaces them with mailto links' do + _, message_html = build_commit_message( + trailer: trailer, + name: FFaker::Name.name, + email: email + ) - doc = filter(message_html) + doc = filter(message_html) - expect_to_have_mailto_link(doc, email: email, trailer: trailer) + expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer) + end + end + + context 'when Gravatar is disabled' do + before do + stub_application_setting(gravatar_enabled: false) + end + + it_behaves_like 'mailto links' + end + + context 'when Gravatar is enabled' do + before do + stub_application_setting(gravatar_enabled: true) + end + + it_behaves_like 'mailto links' + end end it 'multiple trailers in the same message' do @@ -69,7 +89,7 @@ describe Banzai::Filter::CommitTrailersFilter do doc = filter(message) expect_to_have_user_link_with_avatar(doc, user: user, trailer: trailer) - expect_to_have_mailto_link(doc, email: email, trailer: different_trailer) + expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: different_trailer) end context 'special names' do @@ -90,7 +110,7 @@ describe Banzai::Filter::CommitTrailersFilter do doc = filter(message_html) - expect_to_have_mailto_link(doc, email: email, trailer: trailer) + expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer) expect(doc.text).to match Regexp.escape(message) end end diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb index f128c1d4ca4..e2bb378f663 100644 --- a/spec/lib/gitlab/ci/status/build/play_spec.rb +++ b/spec/lib/gitlab/ci/status/build/play_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' describe Gitlab::Ci::Status::Build::Play do let(:user) { create(:user) } - let(:project) { build.project } - let(:build) { create(:ci_build, :manual) } + let(:project) { create(:project, :stubbed_repository) } + let(:build) { create(:ci_build, :manual, project: project) } let(:status) { Gitlab::Ci::Status::Core.new(build, user) } subject { described_class.new(status) } @@ -46,6 +46,8 @@ describe Gitlab::Ci::Status::Build::Play do context 'when user can not push to the branch' do before do build.project.add_developer(user) + create(:protected_branch, :masters_can_push, + name: build.ref, project: project) end it { is_expected.not_to have_action } diff --git a/spec/lib/gitlab/user_access_spec.rb b/spec/lib/gitlab/user_access_spec.rb index 40c8286b1b9..97b6069f64d 100644 --- a/spec/lib/gitlab/user_access_spec.rb +++ b/spec/lib/gitlab/user_access_spec.rb @@ -32,6 +32,12 @@ describe Gitlab::UserAccess do let(:empty_project) { create(:project_empty_repo) } let(:project_access) { described_class.new(user, project: empty_project) } + it 'returns true for admins' do + user.update!(admin: true) + + expect(access.can_push_to_branch?('master')).to be_truthy + end + it 'returns true if user is master' do empty_project.add_master(user) @@ -71,6 +77,12 @@ describe Gitlab::UserAccess do let(:branch) { create :protected_branch, project: project, name: "test" } let(:not_existing_branch) { create :protected_branch, :developers_can_merge, project: project } + it 'returns true for admins' do + user.update!(admin: true) + + expect(access.can_push_to_branch?(branch.name)).to be_truthy + end + it 'returns true if user is a master' do project.add_master(user) diff --git a/spec/lib/omni_auth/strategies/jwt_spec.rb b/spec/lib/omni_auth/strategies/jwt_spec.rb new file mode 100644 index 00000000000..23485fbcb18 --- /dev/null +++ b/spec/lib/omni_auth/strategies/jwt_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper' + +describe OmniAuth::Strategies::Jwt do + include Rack::Test::Methods + include DeviseHelpers + + context '.decoded' do + let(:strategy) { described_class.new({}) } + let(:timestamp) { Time.now.to_i } + let(:jwt_config) { Devise.omniauth_configs[:jwt] } + let(:key) { JWT.encode(claims, jwt_config.strategy.secret) } + + let(:claims) do + { + id: 123, + name: "user_example", + email: "user@example.com", + iat: timestamp + } + end + + before do + allow_any_instance_of(OmniAuth::Strategy).to receive(:options).and_return(jwt_config.strategy) + allow_any_instance_of(Rack::Request).to receive(:params).and_return({ 'jwt' => key }) + end + + it 'decodes the user information' do + result = strategy.decoded + + expect(result["id"]).to eq(123) + expect(result["name"]).to eq("user_example") + expect(result["email"]).to eq("user@example.com") + expect(result["iat"]).to eq(timestamp) + end + + context 'required claims is missing' do + let(:claims) do + { + id: 123, + email: "user@example.com", + iat: timestamp + } + end + + it 'raises error' do + expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid) + end + end + + context 'when valid_within is specified but iat attribute is missing in response' do + let(:claims) do + { + id: 123, + name: "user_example", + email: "user@example.com" + } + end + + before do + jwt_config.strategy.valid_within = Time.now.to_i + end + + it 'raises error' do + expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid) + end + end + + context 'when timestamp claim is too skewed from present' do + let(:claims) do + { + id: 123, + name: "user_example", + email: "user@example.com", + iat: timestamp - 10.minutes.to_i + } + end + + before do + jwt_config.strategy.valid_within = 2.seconds + end + + it 'raises error' do + expect { strategy.decoded }.to raise_error(OmniAuth::Strategies::JWT::ClaimInvalid) + end + end + end +end diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb index 3696e6f62fd..9faf21bfbbd 100644 --- a/spec/models/concerns/avatarable_spec.rb +++ b/spec/models/concerns/avatarable_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Avatarable do - set(:project) { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) } + let(:project) { create(:project, :with_avatar) } let(:gitlab_host) { "https://gitlab.example.com" } let(:relative_url_root) { "/gitlab" } @@ -37,11 +37,23 @@ describe Avatarable do project.visibility_level = visibility_level end - let(:avatar_path) { (avatar_path_prefix + [project.avatar.url]).join } + let(:avatar_path) { (avatar_path_prefix + [project.avatar.local_url]).join } it 'returns the expected avatar path' do expect(project.avatar_path(only_path: only_path)).to eq(avatar_path) end + + context "when avatar is stored remotely" do + before do + stub_uploads_object_storage(AvatarUploader) + + project.avatar.migrate!(ObjectStorage::Store::REMOTE) + end + + it 'returns the expected avatar path' do + expect(project.avatar_url(only_path: only_path)).to eq(avatar_path) + end + end end end end diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 56161bfcc28..25d6597084c 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Environment do - let(:project) { create(:project) } + let(:project) { create(:project, :stubbed_repository) } subject(:environment) { create(:environment, project: project) } it { is_expected.to belong_to(:project) } @@ -201,7 +201,7 @@ describe Environment do end describe '#stop_with_action!' do - let(:user) { create(:admin) } + let(:user) { create(:user) } subject { environment.stop_with_action!(user) } diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb index ba06ff42d87..6e35511e848 100644 --- a/spec/models/lfs_object_spec.rb +++ b/spec/models/lfs_object_spec.rb @@ -62,9 +62,7 @@ describe LfsObject do .with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric)) .once - lfs_object = create(:lfs_object) - lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") - lfs_object.save! + create(:lfs_object, :with_file) end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 2675c2f52c1..e3053a2b86e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1483,52 +1483,6 @@ describe Project do end end - describe '#user_can_push_to_empty_repo?' do - let(:project) { create(:project) } - let(:user) { create(:user) } - - it 'returns false when default_branch_protection is in full protection and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_FULL) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - - it 'returns false when default_branch_protection only lets devs merge and user is dev' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - - it 'returns true when default_branch_protection lets devs push and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns true when default_branch_protection is unprotected and user is developer' do - project.add_developer(user) - stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns true when user is master' do - project.add_master(user) - - expect(project.user_can_push_to_empty_repo?(user)).to be_truthy - end - - it 'returns false when the repo is not empty' do - project.add_master(user) - expect(project).to receive(:empty_repo?).and_return(false) - - expect(project.user_can_push_to_empty_repo?(user)).to be_falsey - end - end - describe '#container_registry_url' do let(:project) { create(:project) } diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb index b4d25e06d9a..9b5c290b9f9 100644 --- a/spec/policies/group_policy_spec.rb +++ b/spec/policies/group_policy_spec.rb @@ -7,9 +7,9 @@ describe GroupPolicy do let(:master) { create(:user) } let(:owner) { create(:user) } let(:admin) { create(:admin) } - let(:group) { create(:group) } + let(:group) { create(:group, :private) } - let(:guest_permissions) { [:read_group, :upload_file, :read_namespace] } + let(:guest_permissions) { [:read_label, :read_group, :upload_file, :read_namespace] } let(:reporter_permissions) { [:admin_label] } @@ -50,6 +50,7 @@ describe GroupPolicy do end context 'with no user' do + let(:group) { create(:group, :public) } let(:current_user) { nil } it do @@ -63,6 +64,28 @@ describe GroupPolicy do end end + context 'has projects' do + let(:current_user) { create(:user) } + let(:project) { create(:project, namespace: group) } + + before do + project.add_developer(current_user) + end + + it do + expect_allowed(:read_group, :read_label) + end + + context 'in subgroups', :nested_groups do + let(:subgroup) { create(:group, :private, parent: group) } + let(:project) { create(:project, namespace: subgroup) } + + it do + expect_allowed(:read_group, :read_label) + end + end + end + context 'guests' do let(:current_user) { guest } diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index 55962f345d4..f6e24d5c96c 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -208,6 +208,17 @@ describe ProjectPresenter do it 'returns nil if user cannot push' do expect(presenter.new_file_anchor_data).to be_nil end + + context 'when the project is empty' do + let(:project) { create(:project, :empty_repo) } + + # Since we protect the default branch for empty repos + it 'is empty for a developer' do + project.add_developer(user) + + expect(presenter.new_file_anchor_data).to be_nil + end + end end describe '#readme_anchor_data' do diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb index 6bed8e812c0..cd1a6cfc427 100644 --- a/spec/requests/openid_connect_spec.rb +++ b/spec/requests/openid_connect_spec.rb @@ -153,4 +153,13 @@ describe 'OpenID Connect requests' do end end end + + context 'OpenID configuration information' do + it 'correctly returns the configuration' do + get '/.well-known/openid-configuration' + + expect(response).to have_gitlab_http_status(200) + expect(json_response).to have_key('issuer') + end + end end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 6ce75c65c8c..f1acfc48468 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -235,6 +235,8 @@ describe Ci::RetryPipelineService, '#execute' do context 'when user is not allowed to trigger manual action' do before do project.add_developer(user) + create(:protected_branch, :masters_can_push, + name: pipeline.ref, project: project) end context 'when there is a failed manual action present' do diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index 1b6caeab15d..ac1e2a03887 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -153,11 +153,13 @@ describe Projects::UpdatePagesService do expect(execute).not_to eq(:success) end - it 'fails for empty file fails' do - build.job_artifacts_archive.update_attributes(file: empty_file) + context 'when using empty file' do + let(:file) { empty_file } - expect { execute } - .to raise_error(Projects::UpdatePagesService::FailedToExtractError) + it 'fails to extract' do + expect { execute } + .to raise_error(Projects::UpdatePagesService::FailedToExtractError) + end end context 'when timeout happens by DNS error' do diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb index add359946db..efa317fd2f9 100644 --- a/spec/support/commit_trailers_spec_helper.rb +++ b/spec/support/commit_trailers_spec_helper.rb @@ -8,7 +8,7 @@ module CommitTrailersSpecHelper expect(wrapper.attribute('data-user').value).to eq user.id.to_s end - def expect_to_have_mailto_link(doc, email:, trailer:) + def expect_to_have_mailto_link_with_avatar(doc, email:, trailer:) wrapper = find_user_wrapper(doc, trailer) expect_to_have_links_with_url_and_avatar(wrapper, "mailto:#{CGI.escape_html(email)}", email) diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb index a2fb3886610..9f28510c3e4 100644 --- a/spec/uploaders/lfs_object_uploader_spec.rb +++ b/spec/uploaders/lfs_object_uploader_spec.rb @@ -46,8 +46,7 @@ describe LfsObjectUploader do end describe 'remote file' do - let(:remote) { described_class::Store::REMOTE } - let(:lfs_object) { create(:lfs_object, file_store: remote) } + let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) } context 'with object storage enabled' do before do @@ -57,16 +56,11 @@ describe LfsObjectUploader do it 'can store file remotely' do allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async) - store_file(lfs_object) + lfs_object - expect(lfs_object.file_store).to eq remote + expect(lfs_object.file_store).to eq(described_class::Store::REMOTE) expect(lfs_object.file.path).not_to be_blank end end end - - def store_file(lfs_object) - lfs_object.file = fixture_file_upload(Rails.root.join("spec/fixtures/dk.png"), "`/png") - lfs_object.save! - end end |