diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-14 12:09:13 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-14 12:09:13 +0300 |
commit | efcfe56681dc8bd586e6ef56d1dc7df05a93197d (patch) | |
tree | fa828ffc634b986aad47ed7ea71adb7b838bc0df /lib | |
parent | e761659df2e0bced8ccb707c87aa350c9d30f18d (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'lib')
-rw-r--r-- | lib/api/api.rb | 9 | ||||
-rw-r--r-- | lib/api/entities/metadata.rb | 15 | ||||
-rw-r--r-- | lib/api/helpers/open_api.rb | 19 | ||||
-rw-r--r-- | lib/api/metadata.rb | 47 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/C++.gitlab-ci.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/Clojure.gitlab-ci.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/Crystal.gitlab-ci.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/Django.gitlab-ci.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml | 5 | ||||
-rw-r--r-- | lib/gitlab/git/wiki.rb | 127 | ||||
-rw-r--r-- | lib/gitlab/git/wiki_page.rb | 16 | ||||
-rw-r--r-- | lib/gitlab/gitaly_client/wiki_service.rb | 169 | ||||
-rw-r--r-- | lib/gitlab/middleware/go.rb | 35 | ||||
-rw-r--r-- | lib/tasks/gitlab/openapi.rake | 23 |
14 files changed, 150 insertions, 335 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb index 28d9bddf81c..933c3f69075 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -3,6 +3,7 @@ module API class API < ::API::Base include APIGuard + include Helpers::OpenApi LOG_FILENAME = Rails.root.join("log", "api_json.log") @@ -165,6 +166,13 @@ module API ::Users::ActivityService.new(@current_user).execute end + # Mount endpoints to include in the OpenAPI V2 documentation here + namespace do + mount ::API::Metadata + + add_open_api_documentation! + end + # Keep in alphabetical order mount ::API::AccessRequests mount ::API::Admin::BatchedBackgroundMigrations @@ -250,7 +258,6 @@ module API mount ::API::MergeRequestApprovals mount ::API::MergeRequestDiffs mount ::API::MergeRequests - mount ::API::Metadata mount ::API::Metrics::Dashboard::Annotations mount ::API::Metrics::UserStarredDashboards mount ::API::Namespaces diff --git a/lib/api/entities/metadata.rb b/lib/api/entities/metadata.rb new file mode 100644 index 00000000000..daa491ec42a --- /dev/null +++ b/lib/api/entities/metadata.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module API + module Entities + class Metadata < Grape::Entity + expose :version + expose :revision + expose :kas do + expose :enabled, documentation: { type: 'boolean' } + expose :externalUrl + expose :version + end + end + end +end diff --git a/lib/api/helpers/open_api.rb b/lib/api/helpers/open_api.rb new file mode 100644 index 00000000000..11602244b57 --- /dev/null +++ b/lib/api/helpers/open_api.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module API + module Helpers + module OpenApi + extend ActiveSupport::Concern + + class_methods do + def add_open_api_documentation! + return if Rails.env.production? + + open_api_config = YAML.load_file(Rails.root.join('config/open_api.yml'))['metadata'].deep_symbolize_keys + + add_swagger_documentation(open_api_config) + end + end + end + end +end diff --git a/lib/api/metadata.rb b/lib/api/metadata.rb index 747082430a9..3e42ffe336a 100644 --- a/lib/api/metadata.rb +++ b/lib/api/metadata.rb @@ -35,8 +35,30 @@ module API end end - desc 'Get the metadata information of the GitLab instance.' do + desc 'Retrieve metadata information for this GitLab instance.' do detail 'This feature was introduced in GitLab 15.2.' + success [ + { + code: 200, + model: Entities::Metadata, + message: 'successful operation', + examples: { + successful_response: { + 'value' => { + version: "15.0-pre", + revision: "c401a659d0c", + kas: { + enabled: true, + externalUrl: "grpc://gitlab.example.com:8150", + version: "15.0.0" + } + } + } + } + } + ] + failure [{ code: 401, message: 'unauthorized operation' }] + tags %w[metadata] end get '/metadata' do run_metadata_query @@ -47,7 +69,30 @@ module API desc 'Get the version information of the GitLab instance.' do detail 'This feature was introduced in GitLab 8.13 and deprecated in 15.5. ' \ 'We recommend you instead use the Metadata API.' + success [ + { + code: 200, + model: Entities::Metadata, + message: 'successful operation', + examples: { + 'Example' => { + 'value' => { + version: "15.0-pre", + revision: "c401a659d0c", + kas: { + enabled: true, + externalUrl: "grpc://gitlab.example.com:8150", + version: "15.0.0" + } + } + } + } + } + ] + failure [{ code: 401, message: 'unauthorized operation' }] + tags %w[metadata] end + get '/version' do run_metadata_query end diff --git a/lib/gitlab/ci/templates/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml index 3096af1b173..fbdaeecca5d 100644 --- a/lib/gitlab/ci/templates/C++.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml @@ -33,3 +33,8 @@ test: stage: test script: - ./runmytests.sh + +deploy: + stage: deploy + script: echo "Define your deployment script!" + environment: production diff --git a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml index 4fe37ceaeaa..3379ce2f649 100644 --- a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml @@ -28,3 +28,8 @@ test: # If you need to run any migrations or configure the database, this # would be the point to do it. - lein test + +deploy: + stage: deploy + script: echo "Define your deployment script!" + environment: production diff --git a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml index 68b55b782cd..9584ec5deef 100644 --- a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml @@ -42,3 +42,8 @@ spec: minitest: script: - crystal test/spec_test.cr # change to the file(s) you execute for tests + +deploy: + stage: deploy + script: echo "Define your deployment script!" + environment: production diff --git a/lib/gitlab/ci/templates/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml index acc4a9d2917..21dda92257e 100644 --- a/lib/gitlab/ci/templates/Django.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml @@ -74,3 +74,8 @@ django-tests: - echo "GRANT ALL on *.* to '${MYSQL_USER}';"| mysql -u root --password="${MYSQL_ROOT_PASSWORD}" -h mysql # use python3 explicitly. see https://wiki.ubuntu.com/Python/3 - python3 manage.py test + +deploy: + stage: deploy + script: echo "Define your deployment script!" + environment: production diff --git a/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml index b8d284532bd..5fcbb251672 100644 --- a/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/dotNET-Core.gitlab-ci.yml @@ -111,3 +111,8 @@ tests: # (e.g. integration tests, unit tests etc). script: - 'dotnet test --no-restore' + +deploy: + stage: deploy + script: echo "Define your deployment script!" + environment: production diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb deleted file mode 100644 index cbe6ba7721a..00000000000 --- a/lib/gitlab/git/wiki.rb +++ /dev/null @@ -1,127 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Git - class Wiki - include Gitlab::Git::WrapsGitalyErrors - - DuplicatePageError = Class.new(StandardError) - - DEFAULT_PAGINATION = Kaminari.config.default_per_page - - CommitDetails = Struct.new(:user_id, :username, :name, :email, :message) do - def to_h - { user_id: user_id, username: username, name: name, email: email, message: message } - end - end - - # GollumSlug inlines just enough knowledge from Gollum::Page to generate a - # slug, which is used when previewing pages that haven't been persisted - class GollumSlug - class << self - def cname(name, char_white_sub = '-', char_other_sub = '-') - if name.respond_to?(:gsub) - name.gsub(/\s/, char_white_sub).gsub(/[<>+]/, char_other_sub) - else - '' - end - end - - def format_to_ext(format) - format == :markdown ? "md" : format.to_s - end - - def canonicalize_filename(filename) - ::File.basename(filename, ::File.extname(filename)).tr('-', ' ') - end - - def generate(title, format) - ext = format_to_ext(format.to_sym) - name = cname(title) + '.' + ext - canonical_name = canonicalize_filename(name) - - path = - if name.include?('/') - name.sub(%r{/[^/]+$}, '/') - else - '' - end - - path + cname(canonical_name, '-', '-') - end - end - end - - attr_reader :repository - - # TODO remove argument when issue - # https://gitlab.com/gitlab-org/gitlab/-/issues/329190 - # is closed. - def self.default_ref(container = nil) - Gitlab::DefaultBranch.value(object: container) - end - - # Initialize with a Gitlab::Git::Repository instance - def initialize(repository) - @repository = repository - end - - def repository_exists? - @repository.exists? - end - - def list_pages(limit: 0, sort: nil, direction_desc: false, load_content: false) - wrapped_gitaly_errors do - gitaly_list_pages( - limit: limit, - sort: sort, - direction_desc: direction_desc, - load_content: load_content - ) - end - end - - def page(title:, version: nil, dir: nil, load_content: true) - wrapped_gitaly_errors do - gitaly_find_page(title: title, version: version, dir: dir, load_content: load_content) - end - end - - def preview_slug(title, format) - GollumSlug.generate(title, format) - end - - private - - def gitaly_wiki_client - @gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository) - end - - def gitaly_find_page(title:, version: nil, dir: nil, load_content: true) - return unless title.present? - - wiki_page, version = gitaly_wiki_client.find_page(title: title, version: version, dir: dir, load_content: load_content) - return unless wiki_page - - Gitlab::Git::WikiPage.from_gitaly_wiki_page(wiki_page, version) - rescue GRPC::InvalidArgument - nil - end - - def gitaly_list_pages(limit: 0, sort: nil, direction_desc: false, load_content: false) - params = { limit: limit, sort: sort, direction_desc: direction_desc } - - gitaly_pages = - if load_content - gitaly_wiki_client.load_all_pages(**params) - else - gitaly_wiki_client.list_all_pages(**params) - end - - gitaly_pages.map do |wiki_page, version| - Gitlab::Git::WikiPage.from_gitaly_wiki_page(wiki_page, version) - end - end - end - end -end diff --git a/lib/gitlab/git/wiki_page.rb b/lib/gitlab/git/wiki_page.rb index 154c79f56b8..26d15daf093 100644 --- a/lib/gitlab/git/wiki_page.rb +++ b/lib/gitlab/git/wiki_page.rb @@ -5,22 +5,6 @@ module Gitlab class WikiPage attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :historical, :formatted_data - class << self - # Abstracts away Gitlab::GitalyClient::WikiPage - def from_gitaly_wiki_page(gitaly_page, version) - new( - url_path: gitaly_page.url_path, - title: gitaly_page.title, - format: gitaly_page.format, - path: gitaly_page.path, - raw_data: gitaly_page.raw_data, - name: gitaly_page.name, - historical: gitaly_page.historical?, - version: version - ) - end - end - def initialize(hash) @url_path = hash[:url_path] @title = hash[:title] diff --git a/lib/gitlab/gitaly_client/wiki_service.rb b/lib/gitlab/gitaly_client/wiki_service.rb deleted file mode 100644 index ca839b232cf..00000000000 --- a/lib/gitlab/gitaly_client/wiki_service.rb +++ /dev/null @@ -1,169 +0,0 @@ -# frozen_string_literal: true - -require 'stringio' - -module Gitlab - module GitalyClient - class WikiService - include Gitlab::EncodingHelper - - MAX_MSG_SIZE = 128.kilobytes.freeze - - def initialize(repository) - @gitaly_repo = repository.gitaly_repository - @repository = repository - end - - def write_page(name, format, content, commit_details) - request = Gitaly::WikiWritePageRequest.new( - repository: @gitaly_repo, - name: encode_binary(name), - format: format.to_s, - commit_details: gitaly_commit_details(commit_details) - ) - - strio = binary_io(content) - - enum = Enumerator.new do |y| - until strio.eof? - request.content = strio.read(MAX_MSG_SIZE) - - y.yield request - - request = Gitaly::WikiWritePageRequest.new - end - end - - response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_write_page, enum, timeout: GitalyClient.medium_timeout) - if error = response.duplicate_error.presence - raise Gitlab::Git::Wiki::DuplicatePageError, error - end - end - - def update_page(page_path, title, format, content, commit_details) - request = Gitaly::WikiUpdatePageRequest.new( - repository: @gitaly_repo, - page_path: encode_binary(page_path), - title: encode_binary(title), - format: format.to_s, - commit_details: gitaly_commit_details(commit_details) - ) - - strio = binary_io(content) - - enum = Enumerator.new do |y| - until strio.eof? - request.content = strio.read(MAX_MSG_SIZE) - - y.yield request - - request = Gitaly::WikiUpdatePageRequest.new - end - end - - GitalyClient.call(@repository.storage, :wiki_service, :wiki_update_page, enum, timeout: GitalyClient.medium_timeout) - end - - def find_page(title:, version: nil, dir: nil, load_content: true) - request = Gitaly::WikiFindPageRequest.new( - repository: @gitaly_repo, - title: encode_binary(title), - revision: encode_binary(version), - directory: encode_binary(dir), - skip_content: !load_content - ) - - response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_find_page, request, timeout: GitalyClient.fast_timeout) - - wiki_page_from_iterator(response) - end - - def list_all_pages(limit: 0, sort: nil, direction_desc: false) - sort_value = Gitaly::WikiListPagesRequest::SortBy.resolve(sort.to_s.upcase.to_sym) - - params = { repository: @gitaly_repo, limit: limit, direction_desc: direction_desc } - params[:sort] = sort_value if sort_value - - request = Gitaly::WikiListPagesRequest.new(params) - stream = GitalyClient.call(@repository.storage, :wiki_service, :wiki_list_pages, request, timeout: GitalyClient.medium_timeout) - stream.each_with_object([]) do |message, pages| - page = message.page - - next unless page - - wiki_page = GitalyClient::WikiPage.new(page.to_h) - version = new_wiki_page_version(page.version) - - pages << [wiki_page, version] - end - end - - def load_all_pages(limit: 0, sort: nil, direction_desc: false) - sort_value = Gitaly::WikiGetAllPagesRequest::SortBy.resolve(sort.to_s.upcase.to_sym) - - params = { repository: @gitaly_repo, limit: limit, direction_desc: direction_desc } - params[:sort] = sort_value if sort_value - - request = Gitaly::WikiGetAllPagesRequest.new(params) - response = GitalyClient.call(@repository.storage, :wiki_service, :wiki_get_all_pages, request, timeout: GitalyClient.medium_timeout) - - pages = [] - - loop do - page, version = wiki_page_from_iterator(response) { |message| message.end_of_page } - - break unless page && version - - pages << [page, version] - end - - pages - end - - private - - # If a block is given and the yielded value is truthy, iteration will be - # stopped early at that point; else the iterator is consumed entirely. - # The iterator is traversed with `next` to allow resuming the iteration. - def wiki_page_from_iterator(iterator) - wiki_page = version = nil - - while message = iterator.next - break if block_given? && yield(message) - - page = message.page - next unless page - - if wiki_page - wiki_page.raw_data << page.raw_data - else - wiki_page = GitalyClient::WikiPage.new(page.to_h) - - version = new_wiki_page_version(page.version) - end - end - - [wiki_page, version] - rescue StopIteration - [wiki_page, version] - end - - def new_wiki_page_version(version) - Gitlab::Git::WikiPageVersion.new( - Gitlab::Git::Commit.decorate(@repository, version.commit), - version.format - ) - end - - def gitaly_commit_details(commit_details) - Gitaly::WikiCommitDetails.new( - user_id: commit_details.user_id, - user_name: encode_binary(commit_details.username), - name: encode_binary(commit_details.name), - email: encode_binary(commit_details.email), - message: encode_binary(commit_details.message) - ) - end - end - end -end diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb index dcbb4557377..e7b4084d153 100644 --- a/lib/gitlab/middleware/go.rb +++ b/lib/gitlab/middleware/go.rb @@ -2,6 +2,9 @@ # A dumb middleware that returns a Go HTML document if the go-get=1 query string # is used irrespective if the namespace/project exists +# +# In order to prevent the project from being exposed, +# auth failure (401 & 403) is regarded as not found (404) module Gitlab module Middleware class Go @@ -21,15 +24,16 @@ module Gitlab rescue Gitlab::Auth::IpBlacklisted Gitlab::AuthLogger.error( message: 'Rack_Attack', - status: 403, env: :blocklist, remote_ip: request.ip, request_method: request.request_method, path: request.fullpath ) - Rack::Response.new('', 403).finish + Rack::Response.new('', 404).finish rescue Gitlab::Auth::MissingPersonalAccessTokenError - Rack::Response.new('', 401).finish + Rack::Response.new('', 404).finish + rescue ActiveRecord::RecordNotFound + Rack::Response.new('', 404).finish end private @@ -100,7 +104,6 @@ module Gitlab # We find all potential project paths out of the path segments path_segments = path.split('/') - simple_project_path = path_segments.first(2).join('/') project_paths = [] begin @@ -110,28 +113,18 @@ module Gitlab # We see if a project exists with any of these potential paths project = project_for_paths(project_paths, request) - - if project - # If a project is found and the user has access, we return the full project path - [project.full_path, project.default_branch] - else - # If not, we return the first two components as if it were a simple `namespace/project` path, - # so that we don't reveal the existence of a nested project the user doesn't have access to. - # This means that for an unauthenticated request to `group/subgroup/project/subpackage` - # for a private `group/subgroup/project` with subpackage path `subpackage`, GitLab will respond - # as if the user is looking for project `group/subgroup`, with subpackage path `project/subpackage`. - # Since `go get` doesn't authenticate by default, this means that - # `go get gitlab.com/group/subgroup/project/subpackage` will not work for private projects. - # `go get gitlab.com/group/subgroup/project.git/subpackage` will work, since Go is smart enough - # to figure that out. `import 'gitlab.com/...'` behaves the same as `go get`. - [simple_project_path, 'master'] - end + # If a project is found and the user has access, we return the full project path + [project.full_path, project.default_branch] end def project_for_paths(paths, request) project = Project.where_full_path_in(paths).first - return unless authentication_result(request, project).can_perform_action_on_project?(:read_project, project) + raise ActiveRecord::RecordNotFound unless project + + unless authentication_result(request, project).can_perform_action_on_project?(:read_project, project) + raise Gitlab::Auth::MissingPersonalAccessTokenError + end project end diff --git a/lib/tasks/gitlab/openapi.rake b/lib/tasks/gitlab/openapi.rake new file mode 100644 index 00000000000..fd067a1bf0b --- /dev/null +++ b/lib/tasks/gitlab/openapi.rake @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'logger' + +if Rails.env.development? + require 'grape-swagger/rake/oapi_tasks' + GrapeSwagger::Rake::OapiTasks.new('::API::API') +end + +namespace :gitlab do + namespace :openapi do + task :generate do + raise 'This task can only be run in the development environment' unless Rails.env.development? + + ENV['store'] = 'tmp/openapi.json' + Rake::Task["oapi:fetch"].invoke(['openapi.json']) + + yaml_content = Gitlab::Json.parse(File.read('tmp/openapi_swagger_doc.json')).to_yaml + + File.write("doc/api/openapi/openapi_v2.yaml", yaml_content) + end + end +end |