Code Monkey home page Code Monkey logo

outside-collaborators's Introduction

Automatically Manage Outside Collaborators Organization-wide

ℹ Intro

Unfortunately, GitHub does not provide (yet) a centralized way to manage outside collaborators within an organization, although this feature is very much requested. As of now, outside collaborators can be handled only at the level of the single repositories, having the main drawback of spreading in many places the knowledge of who can access what.

This undesired effect poses a problem of maintenance. If an external group of developers collaborates to multiple repositories within an organization, what happens if the group evolves over time with new additions or with members who leave? What if it is required to change their access permissions? In short, one must visit all the repositories where such a collaboration takes place for checking and updating the corresponding information.

A possible workaround foresees to invite outside collaborators to join a dedicated organization team. This way, we can take advantage of the perks we all know:

  • teams are maintained within the central settings of the organization
  • teams can be mentioned

Nonetheless, being a formal member of the organization may give the outside collaborator privileges when it comes down to some specific access policies. For instance, if the base permissions of the organization is set to "Read" instead of "None", then that collaborator will be able to clone and pull all repositories, private ones included!

Also, keeping the clear separation among organization members and outside collaborators is certainly advantageous if we consider that we will prevent outside developers from inheriting future upcoming functionalities that GitHub will design for org members and that can turn out to be disruptive when assigned to "unintended" members.

A different solution to the problem is to implement an automated workflow for handling outside collaborators within an organization from a central "dashboard" repo.

⚡ How it works

Components and Architecture

We make use of the following components:

  • GitHub Actions for carrying out in the cloud the necessary jobs underlying the automation.
  • GitHub REST API for querying and setting the information related to the outside collaborators.

The architecture relies on this repository acting as the central dashboard where:

  1. "groups" of outside collaborators (let's call them groups to differentiate from org teams) can be set up and modified using the mechanism of pull-requests.
  2. permissions to access those repositories where the collaboration takes place ("automated repositories") are stored and used to override the standard methodology.

The workflow

The "outside collaborators groups" are defined in YAML files under groups as collections of outside collaborators' usernames.

Here's a simple example:

lab_xyz/group01:
  - "user01"
  - "user02"
  - "user03"

lab_xyz/group02:
  - "user04"
  - "user05"
  - "user06"

generic-group:
  - "user07"
  - "user08"

Likewise, access permissions to automated repositories are defined in YAML files under repos as in the following example:

repo_name_1:
  lab_xyz/group01:
    type: "group"
    permissions: "read"

  lab_xyz/group02:
    type: "group"
    permissions: "triage"

  user06:
    type: "user"
    permissions: "write"

repo_name_2:
  lab_abc/group01:
    type: "group"
    permissions: "write"

  user07:
    type: "user"
    permissions: "maintain"

Upon updating those YAML files in the default branch via forks and pull requests or upon a manual trigger, a GitHub workflow propagates the changes to the automated repositories. In detail, for each automated repo, the outside collaborators are automatically invited, removed or updated with the requested permissions.

Importantly, the YAML files can be modified via pull-requests, enabling the representatives responsible for the outside collaborators (who are generally external to the organization) to keep their groups up-to-date. In addition, pull-requests have to be reviewed by org members, thus ensuring that the process can run securely.

Pay attention to the following points:

  • The name of the automated repositories in the YAML files shall not contain the organization (i.e. the repository owner).
  • With specific keys, entries can represent groups but also individuals (e.g. user06), if there exists the requirement to deal with single outside collaborators within the repository.
  • Handling of outside collaborators on an individual basis takes over groups: e.g. for the repo repo_name_1, the user user06 ends up with "write" permissions instead of "triage", as it should have been instead for being a member of lab_xyz/group02.
  • If a user belongs to multiple groups that are all assigned to a single specific repository, then that user will end up receiving permissions according to how those groups get sequentially processed by the automation. To get around this, just handle that user on an individual basis.
  • The managing of outside collaborators of an automated repo will be always overridden by the automatic workflow. Instead, org members can be still added/removed manually as inside collaborators.
  • You can use YAML anchors and aliases to streamline the content of the files.

Mentioning a group

Anyone posting a message in an issue or a PR of a org repository where the outside collaborators automation is established can mention a group using the convention !group-name.

For example, if user01 posts:

Hey !lab_xyz/group01 👋🏻
I've got an exciting news to share with you!

Then, GitHub will reply with:

>Hey lab_xyz/group01 👋🏻
>I've got an exciting news to share with you!

@user01 wanted to notify the following collaborators:

@user02 @user03

To avoid cluttering the thread, the original triggering message is quoted only up to a given extent.

Removing entries from repos

When a repo entry gets removed from repos, the subsequent action won't be able to perform any cleanup of the corresponding repository as the entry is simply missing and thus the action won't find it out. To circumvent this, leave the entry empty for one round to give the action the possibility to perform the required cleanup. Soon afterward, the entry can be safely removed.

There are other smarter ways to get it done automatically (e.g. by comparing HEAD against HEAD~) but this is actually the simplest. Also, consider that if an entry is no longer declared, then it is semantically correct that the automation does no longer handle it.

Obviously, one can also perform a manual cleanup straight away.

Cleaning up stale invitations

Pending invitations self-expire after a few days.

It may happen that certain invitees do not want to join the intended repositories as external collaborators. In this regard, we do not clean up automatically stale invitations to use them as indicators that we shall not spam those invitees with continuous requests at every automation run.

However, it might be convenient sometimes to clean up stale invitations on demand. To this end, one can rely on the manually-triggered workflow Delete Invitations.

⚙ Automation for your organization

Follow the quick guide below if you want to install this automation in your organization:

  1. Create a copy of this dashboard repository in your organization account. To use the mentioning mechanism (optional), it is required to keep this repo public.
  2. Make sure that only org admins can manage the dashboard repository.
  3. One org admin is required to create a (classic) personal access token (PAT) with full repo scope. If you want to go for a fine-grained PAT, make sure to enable the permissions below:
    • Repository permissions
      • Read access to metadata
      • Read and Write access to administration
    • Organization permissions
      • Read access to members
  4. Ceate in the dashboard a secret called OUTSIDE_COLLABORATORS_TOKEN where to store the admin PAT. This PAT will allow the automation to modify the organization repositories.
  5. Enforce the use of a GitHub environment to guarantee security when checking PRs from external forks. Jobs referring to environments can start only if approved by specific reviewers.
  6. Edit the initial content of groups.
  7. For each single repo of your org you aim to apply automation to, do:
    • Create the corresponding file in repos and add up the entries according to your needs.
    • Optionally, if you aim to enable the mentioning mechanism, copy out the content of templates into the repository while preserving the files paths.

✨ You are finally good to go! Remember to manage the update of the outside collaborators through pull requests.

⚠ Known limitations/issues

  • We are required to comply with the GitHub API rate limit rules. In case we hit such a limit, the automation will wait for the reset to take place. To alleviate this, one can follow @JeroenKnoops's suggestion and use a GitHub App to perform API calls.
  • The dashboard repository is required to be public in order to enable the mentioning mechanism. See FAQ for more details.
  • Pending known bugs 🐛

🔳 Outro

We hope that you will find this workflow helpful!

Contributions that improve the automation are more than welcome.

👨🏻‍💻 Maintainers

This repository is maintained by:

@pattacini

outside-collaborators's People

Contributors

arrenglover avatar chiarabartolozzi avatar danielepucci avatar fiorisi avatar gabrielecaddeo avatar giuliap avatar jeroenknoops avatar lornat75 avatar maggia80 avatar mbrunettini avatar miccol avatar paolo-viceconte avatar pattacini avatar randaz81 avatar rar1989 avatar traversaro avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

outside-collaborators's Issues

GH handles with wrong case are not added up

Bug description

GitHub usernames that are not provided with the correct case yield unexpected behaviors, eventually not being added as collaborators.

The automation is not able to trigger any error in this respect.

Steps to reproduce

Happened in #88 with user @SimoneMic, which was written all lowercase instead.

The net result was that the user was first invited and soon afterward the invitation was removed.

See the report of the corresponding action.

Expected behavior

We should deal with this corner case.

Example repository

No response

Additional context

The flaw stems from the fact that the GitHub API is able to deal with user handles that are not provided consistently in terms of their case, whereas the automation relies on Ruby, which in turn is case sensitive when processing hashes.

Mentioning Breaking - Need Info

Hi:

Pretty helpful tool and thank you for building it.

When I try the mentioning logic, it breaks with appending msg.
I have made the management Repo/Dashboard as Public and added the Action Workflow file in the accessing Repo.
Please let me know if anything I am missing?

image

Validate outside collaborators role before changing permissions

Is your feature request related to a problem?

Although the tool is intended for managing outside collaborators it also affects Members.
If manually added members are not declared within the repo's files, they are being removed as users from the repo.

This means we are forced to use the Github Team structure to ensure our members get the right permissions.
As we have quite a granular permission structure for our Enterprise Members, it means we always must create several teams for each repo, which is an unnecessary overhead.

The solution you would like to have available

include a check if the user is an outside collaborator, using like https://docs.github.com/en/rest/orgs/outside-collaborators?apiVersion=2022-11-28

Alternatives you have considered

No response

Additional context

No response

Send out fresh invitations

Is your feature request related to a problem?

As per https://github.blog/changelog/2020-02-05-self-expiring-repository-and-organization-invitations/, invitations will expire after 7 days.

After #39, pending invitations are not automatically refreshed by the automation.

Therefore, we might end up having external collaborators who didn't accept the invitation within the deadline and thus being no longer able to join if we rely purely on the automation. Manual intervention is required instead.

The solution you would like to have available

Make available a manually triggered workflow that forwards again the invitations throughout the selected repos as per the following input:

  • none: don't send invitations anew (default).
  • *: send out fresh invitations for external collaborators on all repos.
  • comma-separated-list of repos (e.g., repo-1, repo-2): send out fresh invitations only for the specified repos.

Alternatives you have considered

No response

Additional context

No response

check-automated-repositories does not warn that an user has been manually removed

Bug description

My assumption is that the check-automated-repositories should be used as read-only action to validate that the collaborators defined in the yml files are in-sync with the actual users within the repos.

But when i use it, i have noticed that it does not give any indication if an user has been manually removed (by an admin) but is still mentioned in the yml

is this by design or a bug?

Steps to reproduce

  1. create a repo
  2. define the repo in the outside-collaborators with an outside user with write access.
  3. commit to main
  4. the outside-collaborators-handler will add the user.
  5. user gets email and accepts it
  6. i run check-automated-repositories workflow
    `Processing automated repository "repo-sandbox/poc"...
  • "user" with permissions "write"
    ...done with "repo-sandbox/poc" ✔
    `
  1. as a repo admin, i manually remove the user from the repo.
  2. i run check-automated-repositories workflow
    `Processing automated repository "repo-sandbox/poc"...
  • "user" with permissions "write"
    ...done with "repo-sandbox/poc" ✔
    `

Expected behavior

Step 8 should indicate that the user is not present in the repo, but defined in the script.

Example repository

Additional context

No response

[mentioning] Do all repositories need access to the PAT of org admin?

In order to get the mentioning working, we need to create an organizational wide secret with the PAT with org admin rights.

This means that all outside collaborators have access to the PAT and can add a little workflow to display the token. This is already a problem for 'normal' users in an organization, but even more for outside collaborators.

I understand the PAT is needed for the outside-collaborators repository with the definations of the groups and outside collaborators. With protected branches this is also no problem.

Question

Do the other repositories also need a PAT with org admin rights?

Context

I'm working for Philips with >2000 developers...

Extend the mentioning mechanism to discussions

Is your feature request related to a problem?

As of now, the mentioning mechanism works with issues and pull requests, but not with discussions as the GH REST API still lacks the related events.

The solution you would like to have available

As soon as the GH API will be extended, trigger the mentioning mechanism upon events generated by discussions.

Alternatives you have considered

None.

Additional context

No response

Judge the possibility to keep previous invitations

Currently, the handler deletes and reinforces pending invitations as per:

get_repo_invitations(repo_full_name).each { |invitation|
invitee = invitation["invitee"]
puts "- Removing invitee \"#{invitee}\""
check_and_wait_until_reset
$client.delete_repository_invitation(repo_full_name, invitation["id"])

It might be worth refactoring this stage in order to delete only those pending invitations that do correspond to removed contributors.

Action breaks when a user is invited more than once

Hey I found this solution is very helpful! One thing I want to check is when you have multiple group permissions for a single repo, do you have to ensure a given user must only present in one group? Taking the README example:

lab_xyz/group01:
  - "user01"
  - "user02"
  - "user03"

lab_xyz/group02:
  - "user04"
  - "user05"
  - "user06"

If you got group02 like this instead:

lab_xyz/group02:
  - "user03"
  - "user04"
  - "user05"
  - "user06"

while having something like this for your repo

repo_name_1:
  lab_xyz/group01:
    type: "group"
    permission: "read"

  lab_xyz/group02:
    type: "group"
    permission: "triage"

You will likely get an error message like:

/var/lib/gems/2.5.0/gems/octokit-4.19.0/lib/octokit/response/raise_error.rb:16:in `on_complete': PATCH https://api.github.com/repos/somerepo/oc-test-4/invitations/33467263: 422 - Invalid request. (Octokit::UnprocessableEntity)

I wonder if this is a limitation that the solution does not support sending invitation twice for a single user during one workflow run. It is not a blocker to me so just want to clarify a bit

Duplicate entries break the update scripts.

Bug description

When someone accidentally adds two the same users in a group, the script will fail.

lab_xyz/group01:
  - "user01"
  - "user02"
  - "user02"

When user02 is invited for the first time, it status will be "Pending Invititation".
When user02 is invited for the second time, the script assumes the invitation is accepted and tries to align / update the permissions. This results in an error.

Steps to reproduce

Create a group with two the same users like:

lab_xyz/group01:
  - "user01"
  - "user02"
  - "user02"

Expected behavior

It should ignore the duplicate entries.

Example repository

Unfortunately I cannot share a repository, because of confidentiality.

This is the error we get:

The script tries to update the permissions of a "pending" invitation.

/usr/local/bundle/gems/octokit-4.21.0/lib/octokit/response/raise_error.rb:14:in `on_complete': PATCH https://api.github.com/repos/<organization>/<repository>/invitations/<number-goes-here>: 422 - Invalid request. (Octokit::UnprocessableEntity)

No subschema in "anyOf" matched.
"permissions" wasn't supplied. // See: https://docs.github.com/rest/reference/repos#update-a-repository-invitation
	from /usr/local/bundle/gems/faraday-1.8.0/lib/faraday/middleware.rb:19:in `block in call'
	from /usr/local/bundle/gems/faraday-1.8.0/lib/faraday/response.rb:59:in `on_complete'
	from /usr/local/bundle/gems/faraday-1.8.0/lib/faraday/middleware.rb:18:in `call'
	from /usr/local/bundle/gems/octokit-4.21.0/lib/octokit/middleware/follow_redirects.rb:73:in `perform_with_redirection'
	from /usr/local/bundle/gems/octokit-4.21.0/lib/octokit/middleware/follow_redirects.rb:61:in `call'
	from /usr/local/bundle/gems/faraday-1.8.0/lib/faraday/request/retry.rb:148:in `call'
	from /usr/local/bundle/gems/faraday-1.8.0/lib/faraday/rack_builder.rb:154:in `build_response'

Additional context

No response

Rate limiter not handled correctly for paged services

Bug description

Pages services such as collaborators(repo) and repository_invitations(repo) may trigger rate limiter errors that currently are not properly handled.

Steps to reproduce

Difficult to reproduce as we need to have the rate limiter expired at the right time.
But seen at least once.

Expected behavior

We're required to handle these error conditions successfully.

Example repository

No response

Additional context

No response

Add Host-coreboot.yml to repos directory and add coreboot users

Is your feature request related to a problem?

Need to add a coreboot YML file to manage read and write group access to the OpenSIL/coreboot repo.

The solution you would like to have available

Creating Host-coreboot.yml file.

Alternatives you have considered

No response

Additional context

No response

Deleting a file from repos does not trigger a proper cleanup

When a YAML file gets removed from repos, the subsequent action won't be able to perform any cleanup of the repository corresponding to that particular file as the file is simply missing and the ruby script won't find it out.

The current workaround is to clean up the repository manually.

Mentioning within PR

We'd need to extend the automatic notification we did for issues also to the case of PR's.

Github action still seems successful even if collaborators weren't added

Bug description

If there are errors adding any users, such as a typo in the repo name, the github action still succeeds. The error shows up with a helpful red X in the log message, but it's not obvious that the logs need to be checked since the job was successful.

In my case, the issue was that I had run out of seats in our paid plan, which also wasn't easy to figure out because the error message was just "problem detected".

Steps to reproduce

  1. Create a new file under repos called dummy.yml (assuming there isn't a repo called dummy!)
dummy:
  some-group:
    type: "group"
    permissions: "read"
  1. Run the "Update Outside Collaborators" job
  2. Job will complete successfully, even though there are errors in the log

Expected behavior

I'd expect the job would fail and the logs would have enough details to diagnose

Example repository

No response

Additional context

I have a simple fix that I can contribute back, but I'm not a Ruby developer, so it might not be idiomatic.

Add collaborators to a list of repos

It would be really nice if we could add a list of collaborators to a list of repos, so that we don't have to copy the same block over and over again:

repoA:
  groupC:
    type: "group"
    permissions: "read"
  userC:
    type: "user"
    permissions: "write"

repoB:
  groupC:
    type: "group"
    permissions: "read"
  userC:
    type: "user"
    permissions: "write"

Is this already possible? I couldn't figure it out, as this wasn't working:

repoA:
repoB:
  groupC:
    type: "group"
    permissions: "read"
  userC:
    type: "user"
    permissions: "write"

Comments within yaml file of group members

Is your feature request related to a problem?

No

The solution you would like to have available

When I revise the list of github accounts to delete/add new ones for some of them I have a hard time remembering to whom they correspond to. If we could add the full name as a comment in the yaml file next to the nick name it would be nice.

Alternatives you have considered

No

Additional context

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.