feat: validate github organization during OAuth login (#6700)
* feat: add GITHUB_ORGANIZATION_ID support for GitHub OAuth integration * fix: remove debug print statements from InstanceConfigurationEndpoint
This commit is contained in:
parent
4032aa62c5
commit
f720a9afb2
4 changed files with 51 additions and 1 deletions
|
|
@ -43,6 +43,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
GITHUB_CLIENT_ID: config["GITHUB_CLIENT_ID"],
|
GITHUB_CLIENT_ID: config["GITHUB_CLIENT_ID"],
|
||||||
GITHUB_CLIENT_SECRET: config["GITHUB_CLIENT_SECRET"],
|
GITHUB_CLIENT_SECRET: config["GITHUB_CLIENT_SECRET"],
|
||||||
|
GITHUB_ORGANIZATION_ID: config["GITHUB_ORGANIZATION_ID"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -93,6 +94,19 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
error: Boolean(errors.GITHUB_CLIENT_SECRET),
|
error: Boolean(errors.GITHUB_CLIENT_SECRET),
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "GITHUB_ORGANIZATION_ID",
|
||||||
|
type: "text",
|
||||||
|
label: "Organization ID",
|
||||||
|
description: (
|
||||||
|
<>
|
||||||
|
The organization github ID.
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
placeholder: "123456789",
|
||||||
|
error: Boolean(errors.GITHUB_ORGANIZATION_ID),
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const GITHUB_SERVICE_FIELD: TCopyField[] = [
|
const GITHUB_SERVICE_FIELD: TCopyField[] = [
|
||||||
|
|
@ -150,6 +164,7 @@ export const InstanceGithubConfigForm: FC<Props> = (props) => {
|
||||||
reset({
|
reset({
|
||||||
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
|
GITHUB_CLIENT_ID: response.find((item) => item.key === "GITHUB_CLIENT_ID")?.value,
|
||||||
GITHUB_CLIENT_SECRET: response.find((item) => item.key === "GITHUB_CLIENT_SECRET")?.value,
|
GITHUB_CLIENT_SECRET: response.find((item) => item.key === "GITHUB_CLIENT_SECRET")?.value,
|
||||||
|
GITHUB_ORGANIZATION_ID: response.find((item) => item.key === "GITHUB_ORGANIZATION_ID")?.value,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => console.error(err));
|
.catch((err) => console.error(err));
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,12 @@ AUTHENTICATION_ERROR_CODES = {
|
||||||
"OAUTH_NOT_CONFIGURED": 5104,
|
"OAUTH_NOT_CONFIGURED": 5104,
|
||||||
"GOOGLE_NOT_CONFIGURED": 5105,
|
"GOOGLE_NOT_CONFIGURED": 5105,
|
||||||
"GITHUB_NOT_CONFIGURED": 5110,
|
"GITHUB_NOT_CONFIGURED": 5110,
|
||||||
|
"GITHUB_USER_NOT_IN_ORG": 5122,
|
||||||
"GITLAB_NOT_CONFIGURED": 5111,
|
"GITLAB_NOT_CONFIGURED": 5111,
|
||||||
"GOOGLE_OAUTH_PROVIDER_ERROR": 5115,
|
"GOOGLE_OAUTH_PROVIDER_ERROR": 5115,
|
||||||
"GITHUB_OAUTH_PROVIDER_ERROR": 5120,
|
"GITHUB_OAUTH_PROVIDER_ERROR": 5120,
|
||||||
"GITLAB_OAUTH_PROVIDER_ERROR": 5121,
|
"GITLAB_OAUTH_PROVIDER_ERROR": 5121,
|
||||||
|
|
||||||
# Reset Password
|
# Reset Password
|
||||||
"INVALID_PASSWORD_TOKEN": 5125,
|
"INVALID_PASSWORD_TOKEN": 5125,
|
||||||
"EXPIRED_PASSWORD_TOKEN": 5130,
|
"EXPIRED_PASSWORD_TOKEN": 5130,
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,16 @@ from plane.authentication.adapter.error import (
|
||||||
class GitHubOAuthProvider(OauthAdapter):
|
class GitHubOAuthProvider(OauthAdapter):
|
||||||
token_url = "https://github.com/login/oauth/access_token"
|
token_url = "https://github.com/login/oauth/access_token"
|
||||||
userinfo_url = "https://api.github.com/user"
|
userinfo_url = "https://api.github.com/user"
|
||||||
|
org_membership_url = f"https://api.github.com/orgs"
|
||||||
|
|
||||||
provider = "github"
|
provider = "github"
|
||||||
scope = "read:user user:email"
|
scope = "read:user user:email"
|
||||||
|
|
||||||
|
organization_scope = "read:org"
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, request, code=None, state=None, callback=None):
|
def __init__(self, request, code=None, state=None, callback=None):
|
||||||
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET = get_configuration_value(
|
GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, GITHUB_ORGANIZATION_ID = get_configuration_value(
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"key": "GITHUB_CLIENT_ID",
|
"key": "GITHUB_CLIENT_ID",
|
||||||
|
|
@ -32,6 +37,10 @@ class GitHubOAuthProvider(OauthAdapter):
|
||||||
"key": "GITHUB_CLIENT_SECRET",
|
"key": "GITHUB_CLIENT_SECRET",
|
||||||
"default": os.environ.get("GITHUB_CLIENT_SECRET"),
|
"default": os.environ.get("GITHUB_CLIENT_SECRET"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "GITHUB_ORGANIZATION_ID",
|
||||||
|
"default": os.environ.get("GITHUB_ORGANIZATION_ID"),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -43,6 +52,10 @@ class GitHubOAuthProvider(OauthAdapter):
|
||||||
|
|
||||||
client_id = GITHUB_CLIENT_ID
|
client_id = GITHUB_CLIENT_ID
|
||||||
client_secret = GITHUB_CLIENT_SECRET
|
client_secret = GITHUB_CLIENT_SECRET
|
||||||
|
self.organization_id = GITHUB_ORGANIZATION_ID
|
||||||
|
|
||||||
|
if self.organization_id:
|
||||||
|
self.scope += f" {self.organization_scope}"
|
||||||
|
|
||||||
redirect_uri = f"""{"https" if request.is_secure() else "http"}://{request.get_host()}/auth/github/callback/"""
|
redirect_uri = f"""{"https" if request.is_secure() else "http"}://{request.get_host()}/auth/github/callback/"""
|
||||||
url_params = {
|
url_params = {
|
||||||
|
|
@ -113,12 +126,26 @@ class GitHubOAuthProvider(OauthAdapter):
|
||||||
error_message="GITHUB_OAUTH_PROVIDER_ERROR",
|
error_message="GITHUB_OAUTH_PROVIDER_ERROR",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def is_user_in_organization(self, github_username):
|
||||||
|
headers = {"Authorization": f"Bearer {self.token_data.get('access_token')}"}
|
||||||
|
response = requests.get(f"{self.org_membership_url}/{self.organization_id}/memberships/{github_username}", headers=headers)
|
||||||
|
return response.status_code == 200 # 200 means the user is a member
|
||||||
|
|
||||||
def set_user_data(self):
|
def set_user_data(self):
|
||||||
user_info_response = self.get_user_response()
|
user_info_response = self.get_user_response()
|
||||||
headers = {
|
headers = {
|
||||||
"Authorization": f"Bearer {self.token_data.get('access_token')}",
|
"Authorization": f"Bearer {self.token_data.get('access_token')}",
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.organization_id:
|
||||||
|
if not self.is_user_in_organization(user_info_response.get("login")):
|
||||||
|
raise AuthenticationException(
|
||||||
|
error_code=AUTHENTICATION_ERROR_CODES["GITHUB_USER_NOT_IN_ORG"],
|
||||||
|
error_message="GITHUB_USER_NOT_IN_ORG",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
email = self.__get_email(headers=headers)
|
email = self.__get_email(headers=headers)
|
||||||
super().set_user_data(
|
super().set_user_data(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,12 @@ class Command(BaseCommand):
|
||||||
"category": "GITHUB",
|
"category": "GITHUB",
|
||||||
"is_encrypted": True,
|
"is_encrypted": True,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"key": "GITHUB_ORGANIZATION_ID",
|
||||||
|
"value": os.environ.get("GITHUB_ORGANIZATION_ID"),
|
||||||
|
"category": "GITHUB",
|
||||||
|
"is_encrypted": False,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"key": "GITLAB_HOST",
|
"key": "GITLAB_HOST",
|
||||||
"value": os.environ.get("GITLAB_HOST"),
|
"value": os.environ.get("GITLAB_HOST"),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue