Skip to content

PoC: Separate Access Model for Deployment Approval (API)

What does this MR do and why?

This MR is on top of !82071 (closed)

This MR is a PoC that represents the backend implementation for Deployment Approval Rules. It's for giving a high-level overview of the entire implementation. This is NOT FOR MERGE.

Related #345678 (closed)

The approach

We add a capability to create Approval Rules via API. If the rules exist, the system switches to the rule-base authorization instead of using deploy access levels for approvals.

We likely follow-up frontend and UX proposal after the backend architecture has been settled.

Screenshots or screen recordings

Creating a new Protected Environment
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: glpat-SNHmsxECHzfGdP4BzCsF" "http://local.gitlab.test:8181/api/v4/projects/261/protected_environments/production" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   287  100   287    0     0     56      0  0:00:05  0:00:05 --:--:--    58
{
  "name": "production",
  "deploy_access_levels": [
    {
      "access_level": 40,
      "access_level_description": "Maintainers",
      "user_id": null,
      "group_id": null
    }
  ],
  "required_approval_count": 0,
  "approval_rules": [
    {
      "user_id": null,
      "group_id": null,
      "access_level": 40,
      "access_level_description": "Maintainers",
      "count": 1
    }
  ]
}
Making sure the deployment is blocked
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: glpat-SNHmsxECHzfGdP4BzCsF" "http://local.gitlab.test:8181/api/v4/projects/261/deployments/1520" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2569    0  2569    0     0   7468      0 --:--:-- --:--:-- --:--:--  7468
{
  "id": 1520,
  "iid": 1,
  "ref": "main",
  "sha": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
  "created_at": "2022-03-10T13:03:37.957Z",
  "updated_at": "2022-03-10T13:03:41.777Z",
  "user": {
    "id": 1,
    "username": "root",
    "name": "Administrator",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/root"
  },
  "environment": {
    "id": 708,
    "name": "production",
    "slug": "production",
    "external_url": null,
    "created_at": "2022-03-10T13:03:37.662Z",
    "updated_at": "2022-03-10T13:03:37.662Z"
  },
  "deployable": {
    "id": 3915,
    "status": "manual",
    "stage": "test",
    "name": "deploy",
    "ref": "main",
    "tag": false,
    "coverage": null,
    "allow_failure": false,
    "created_at": "2022-03-10T13:03:37.709Z",
    "started_at": null,
    "finished_at": null,
    "duration": null,
    "queued_duration": null,
    "user": {
      "id": 1,
      "username": "root",
      "name": "Administrator",
      "state": "active",
      "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
      "web_url": "http://local.gitlab.test:8181/root",
      "created_at": "2021-06-18T09:55:00.193Z",
      "bio": "",
      "location": null,
      "public_email": "",
      "skype": "",
      "linkedin": "",
      "twitter": "",
      "website_url": "",
      "organization": null,
      "job_title": "",
      "pronouns": null,
      "bot": false,
      "work_information": null,
      "followers": 0,
      "following": 0,
      "local_time": "1:50 PM"
    },
    "commit": {
      "id": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
      "short_id": "ac679288",
      "created_at": "2022-03-10T13:01:14.000+00:00",
      "parent_ids": [
        "8a0fd35f780f63908f4fb4e90c2f0a1881605172"
      ],
      "title": "Add new file",
      "message": "Add new file",
      "author_name": "Administrator",
      "author_email": "admin@example.com",
      "authored_date": "2022-03-10T13:01:14.000+00:00",
      "committer_name": "Administrator",
      "committer_email": "admin@example.com",
      "committed_date": "2022-03-10T13:01:14.000+00:00",
      "trailers": {},
      "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/commit/ac67928811755a52fc4e01fac64be5ee1caa77f1"
    },
    "pipeline": {
      "id": 1368,
      "iid": 1,
      "project_id": 261,
      "sha": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
      "ref": "main",
      "status": "manual",
      "source": "web",
      "created_at": "2022-03-10T13:03:37.671Z",
      "updated_at": "2022-03-10T13:03:41.846Z",
      "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/pipelines/1368"
    },
    "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/jobs/3915",
    "artifacts": [],
    "runner": null,
    "artifacts_expire_at": null,
    "tag_list": []
  },
  "status": "blocked",
  "pending_approval_count": 0,
  "approvals": [],
  "approval_summary": [
    {
      "user_id": null,
      "group_id": null,
      "access_level": 40,
      "access_level_description": "Maintainers",
      "count": 1,
      "deployment_approvals": []
    }
  ]
}
Approving a job
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved&comment=Looks good to me" \
>      --header "PRIVATE-TOKEN: glpat-_jA--dPSAaQxvsMUaap5" "http://local.gitlab.test:8181/api/v4/projects/261/deployments/1520/approval" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   358  100   318  100    40    700     88 --:--:-- --:--:-- --:--:--   786
{
  "user": {
    "id": 100,
    "username": "shinya",
    "name": "shinya Maeda",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/d99019d49e480c8654156065b29eeb6f?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/shinya"
  },
  "status": "approved",
  "created_at": "2022-03-10T13:54:21.628Z",
  "comment": "Looks good to me"
}
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --data "status=approved&comment=Looks good to me" \
>      --header "PRIVATE-TOKEN: glpat-_jA--dPSAaQxvsMUaap5" "http://local.gitlab.test:8181/api/v4/projects/261/deployments/1520/approval" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   117  100    77  100    40    484    251 --:--:-- --:--:-- --:--:--   735
{
  "message": "You do not have permission to approve or reject this deployment"
}
Making sure the deployment started running
shinya@shinya-B550-VISION-D:~/workspace/thin-gdk/services/rails/src$ curl --header "PRIVATE-TOKEN: glpat-SNHmsxECHzfGdP4BzCsF" "http://local.gitlab.test:8181/api/v4/projects/261/deployments/1520" | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3215    0  3215    0     0  13680      0 --:--:-- --:--:-- --:--:-- 13680
{
  "id": 1520,
  "iid": 1,
  "ref": "main",
  "sha": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
  "created_at": "2022-03-10T13:03:37.957Z",
  "updated_at": "2022-03-10T13:54:21.648Z",
  "user": {
    "id": 1,
    "username": "root",
    "name": "Administrator",
    "state": "active",
    "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
    "web_url": "http://local.gitlab.test:8181/root"
  },
  "environment": {
    "id": 708,
    "name": "production",
    "slug": "production",
    "external_url": null,
    "created_at": "2022-03-10T13:03:37.662Z",
    "updated_at": "2022-03-10T13:03:37.662Z"
  },
  "deployable": {
    "id": 3915,
    "status": "pending",
    "stage": "test",
    "name": "deploy",
    "ref": "main",
    "tag": false,
    "coverage": null,
    "allow_failure": false,
    "created_at": "2022-03-10T13:03:37.709Z",
    "started_at": null,
    "finished_at": null,
    "duration": null,
    "queued_duration": 20.255790985,
    "user": {
      "id": 1,
      "username": "root",
      "name": "Administrator",
      "state": "active",
      "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
      "web_url": "http://local.gitlab.test:8181/root",
      "created_at": "2021-06-18T09:55:00.193Z",
      "bio": "",
      "location": null,
      "public_email": "",
      "skype": "",
      "linkedin": "",
      "twitter": "",
      "website_url": "",
      "organization": null,
      "job_title": "",
      "pronouns": null,
      "bot": false,
      "work_information": null,
      "followers": 0,
      "following": 0,
      "local_time": "1:54 PM"
    },
    "commit": {
      "id": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
      "short_id": "ac679288",
      "created_at": "2022-03-10T13:01:14.000+00:00",
      "parent_ids": [
        "8a0fd35f780f63908f4fb4e90c2f0a1881605172"
      ],
      "title": "Add new file",
      "message": "Add new file",
      "author_name": "Administrator",
      "author_email": "admin@example.com",
      "authored_date": "2022-03-10T13:01:14.000+00:00",
      "committer_name": "Administrator",
      "committer_email": "admin@example.com",
      "committed_date": "2022-03-10T13:01:14.000+00:00",
      "trailers": {},
      "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/commit/ac67928811755a52fc4e01fac64be5ee1caa77f1"
    },
    "pipeline": {
      "id": 1368,
      "iid": 1,
      "project_id": 261,
      "sha": "ac67928811755a52fc4e01fac64be5ee1caa77f1",
      "ref": "main",
      "status": "pending",
      "source": "web",
      "created_at": "2022-03-10T13:03:37.671Z",
      "updated_at": "2022-03-10T13:54:23.292Z",
      "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/pipelines/1368"
    },
    "web_url": "http://local.gitlab.test:8181/root/deploy-approval-rules/-/jobs/3915",
    "artifacts": [],
    "runner": null,
    "artifacts_expire_at": null,
    "tag_list": []
  },
  "status": "created",
  "pending_approval_count": 0,
  "approvals": [
    {
      "user": {
        "id": 100,
        "username": "shinya",
        "name": "shinya Maeda",
        "state": "active",
        "avatar_url": "https://www.gravatar.com/avatar/d99019d49e480c8654156065b29eeb6f?s=80&d=identicon",
        "web_url": "http://local.gitlab.test:8181/shinya"
      },
      "status": "approved",
      "created_at": "2022-03-10T13:54:21.628Z",
      "comment": "Looks good to me"
    }
  ],
  "approval_summary": [
    {
      "user_id": null,
      "group_id": null,
      "access_level": 40,
      "access_level_description": "Maintainers",
      "count": 1,
      "deployment_approvals": [
        {
          "user": {
            "id": 100,
            "username": "shinya",
            "name": "shinya Maeda",
            "state": "active",
            "avatar_url": "https://www.gravatar.com/avatar/d99019d49e480c8654156065b29eeb6f?s=80&d=identicon",
            "web_url": "http://local.gitlab.test:8181/shinya"
          },
          "status": "approved",
          "created_at": "2022-03-10T13:54:21.628Z",
          "comment": "Looks good to me"
        }
      ]
    }
  ]
}

How to set up and validate locally

Numbered steps to set up and validate the change are strongly suggested.

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Shinya Maeda

Merge request reports