async Task <AzureServicePrincipal> IAdapterAuthorize.ResolvePrincipalAsync(DeploymentScope deploymentScope, HttpRequest request) { const string SignatureHeader = "X-Hub-Signature-256"; if (deploymentScope is null) { throw new ArgumentNullException(nameof(deploymentScope)); } if (request is null) { throw new ArgumentNullException(nameof(request)); } if (request.Headers.TryGetValue(SignatureHeader, out var signatureValue) && !string.IsNullOrEmpty(signatureValue)) { var token = await TokenClient .GetAsync <GitHubToken>(deploymentScope) .ConfigureAwait(false); if (!string.IsNullOrEmpty(token?.WebhookSecret)) { var signature = signatureValue .ToString() .Substring(signatureValue.ToString().IndexOf('=') + 1); // CAUTION - we need to read the body but leave // the request's body stream open so it stays // available for subsequent requests var body = await request .ReadStringAsync(leaveOpen : true) .ConfigureAwait(false); var secret = Encoding.ASCII .GetBytes(token.WebhookSecret); using var hmac = new HMACSHA256(secret); var hash = hmac .ComputeHash(Encoding.ASCII.GetBytes(body)) .ToHexString(); if (hash.Equals(signature)) { // signature successfully validated - lets use the adapters identity to proceed return(await GetServiceIdentityAsync(deploymentScope).ConfigureAwait(false)); } } } return(null); }
public override async Task <bool> IsAuthorizedAsync(DeploymentScope deploymentScope) { if (deploymentScope is null) { throw new ArgumentNullException(nameof(deploymentScope)); } var token = await TokenClient .GetAsync <GitHubToken>(deploymentScope) .ConfigureAwait(false); return(!(token is null)); }
private async Task <GitHubClient> CreateClientAsync(DeploymentScope deploymentScope, string acceptHeader = null) { if (deploymentScope is null) { throw new ArgumentNullException(nameof(deploymentScope)); } var token = await TokenClient .GetAsync <GitHubToken>(deploymentScope) .ConfigureAwait(false); return(await CreateClientAsync(token, acceptHeader) .ConfigureAwait(false)); }
private static async Task TestBasicAuthoriseRequest(TokenClient c, string host) { // now test token is working on method var getTest = await c.GetAsync($"{host}/test/token"); if (getTest.IsSuccessStatusCode) { var content = await getTest.Content.ReadAsStringAsync(); Console.WriteLine("Success" + content); } else { var msg = $"{getTest.StatusCode} - {getTest.ReasonPhrase}"; Console.WriteLine("Error" + msg); } }
public static async Task <RefreshTokenResult> StartRefreshToken() { RefreshTokenResult refresh_token_result = new RefreshTokenResult(); try { string url = String.Format("callback?refresh_token={0}", Settings.Default.RefreshToken); HttpResponseMessage response = await TokenClient.GetAsync(url); response.EnsureSuccessStatusCode(); string response_body = await response.Content.ReadAsStringAsync(); refresh_token_result.Token = JsonConvert.DeserializeObject <Model.Token>(response_body); refresh_token_result.Token.token_time = DateTime.Now; refresh_token_result.Status = 1; return(refresh_token_result); } catch (HttpRequestException e) { refresh_token_result.Status = -1; refresh_token_result.ErrorMessage = e.Message; return(refresh_token_result); } }
private Task <Component> ExecuteAsync(Component component, User contextUser, IAsyncCollector <ICommand> commandQueue, Func <GitHubClient, string, Octokit.Project, Octokit.Team, Octokit.Team, Task <Component> > callback) { if (component is null) { throw new ArgumentNullException(nameof(component)); } if (contextUser is null) { throw new ArgumentNullException(nameof(contextUser)); } if (commandQueue is null) { throw new ArgumentNullException(nameof(commandQueue)); } if (callback is null) { throw new ArgumentNullException(nameof(callback)); } return(base.WithContextAsync(component, async(componentOrganization, componentDeploymentScope, componentProject) => { var token = await TokenClient .GetAsync <GitHubToken>(componentDeploymentScope) .ConfigureAwait(false); var client = await CreateClientAsync(token) .ConfigureAwait(false); var teams = await client.Organization.Team .GetAll(token.OwnerLogin) .ConfigureAwait(false); var tasks = new List <Task>(); var teamsByName = teams .ToDictionary(k => k.Name, v => v); var organizationTeamName = $"TeamCloud-{componentOrganization.Slug}"; var organizationTeam = await EnsureTeamAsync(organizationTeamName, null, new NewTeam(organizationTeamName) { Description = $"TeamCloud {componentOrganization.DisplayName} organization.", Privacy = TeamPrivacy.Closed // Parent and nested child teams must use Closed }).ConfigureAwait(false); var organizationAdminTeamName = $"{organizationTeamName}-Admins"; var organizationAdminTeam = await EnsureTeamAsync(organizationAdminTeamName, organizationTeam, new NewTeam(organizationAdminTeamName) { Description = $"TeamCloud {componentOrganization.DisplayName} organization admins.", Privacy = TeamPrivacy.Closed, // Parent and nested child teams must use Closed Permission = Permission.Admin }).ConfigureAwait(false); var projectTeamName = $"{organizationTeamName}-{componentProject.Slug}"; var projectTeam = await EnsureTeamAsync(projectTeamName, organizationTeam, new NewTeam(projectTeamName) { Description = $"TeamCloud project {componentProject.DisplayName} in the {componentOrganization.DisplayName} organization.", Privacy = TeamPrivacy.Closed // Parent and nested child teams must use Closed }).ConfigureAwait(false); var projectAdminsTeamName = $"{projectTeamName}-Admins"; var projectAdminsTeam = await EnsureTeamAsync(projectAdminsTeamName, projectTeam, new NewTeam(projectAdminsTeamName) { Description = $"TeamCloud project {componentProject.DisplayName} admins in the {componentOrganization.DisplayName} organization.", Privacy = TeamPrivacy.Closed, // Parent and nested child teams must use Closed Permission = Permission.Admin }).ConfigureAwait(false); var projectName = $"TeamCloud-{componentOrganization.Slug}-{componentProject.Slug}"; var project = await EnsureProjectAsync(projectName, new NewProject(projectName) { Body = $"Project for TeamCloud project {componentProject.DisplayName} in organization {componentOrganization.DisplayName}" }).ConfigureAwait(false); var organizationOwners = await userRepository .ListOwnersAsync(component.Organization) .ToArrayAsync() .ConfigureAwait(false); var organizationAdmins = await userRepository .ListAdminsAsync(component.Organization) .ToArrayAsync() .ConfigureAwait(false); await Task.WhenAll( EnsurePermissionAsync(projectTeam, project, "write"), EnsurePermissionAsync(projectAdminsTeam, project, "admin"), SynchronizeTeamMembersAsync(client, organizationAdminTeam, Enumerable.Concat(organizationOwners, organizationAdmins).Distinct(), component, contextUser, commandQueue) ).ConfigureAwait(false); return await callback(client, token.OwnerLogin, project, projectTeam, projectAdminsTeam).ConfigureAwait(false); async Task <Team> EnsureTeamAsync(string teamName, Team parentTeam, NewTeam teamDefinition) { if (!teamsByName.TryGetValue(teamName, out var team)) { await using var adapterLock = await AcquireLockAsync(nameof(GitHubAdapter), component.DeploymentScopeId).ConfigureAwait(false); teamDefinition.ParentTeamId = parentTeam?.Id; try { team = await client.Organization.Team .Create(token.OwnerLogin, teamDefinition) .ConfigureAwait(false); } catch (ApiException exc) when(exc.StatusCode == HttpStatusCode.UnprocessableEntity) // yes, thats the status code if the team already exists { var teams = await client.Organization.Team .GetAll(token.OwnerLogin) .ConfigureAwait(false); team = teams .FirstOrDefault(t => t.Name.Equals(teamName, StringComparison.Ordinal)); if (team is null) { throw; } } } if (team.Parent?.Id != parentTeam?.Id) { await using var adapterLock = await AcquireLockAsync(nameof(GitHubAdapter), component.DeploymentScopeId).ConfigureAwait(false); team = await client.Organization.Team.Update(team.Id, new UpdateTeam(team.Name) { ParentTeamId = parentTeam?.Id ?? 0 }).ConfigureAwait(false); } return team; } async Task <Octokit.Project> EnsureProjectAsync(string projectName, NewProject projectDefinition) { var projects = await client.Repository.Project .GetAllForOrganization(token.OwnerLogin) .ConfigureAwait(false); var project = projects .FirstOrDefault(t => t.Name.Equals(projectName, StringComparison.Ordinal)); try { project ??= await client.Repository.Project .CreateForOrganization(token.OwnerLogin, projectDefinition) .ConfigureAwait(false); } catch (ApiException exc) when(exc.StatusCode == HttpStatusCode.UnprocessableEntity) { // the project already exists - try to re-fetch project information projects = await client.Repository.Project .GetAllForOrganization(token.OwnerLogin) .ConfigureAwait(false); project = projects .FirstOrDefault(t => t.Name.Equals(projectName, StringComparison.Ordinal)); if (project is null) { throw new ApplicationException($"Duplicate project ({projectName}) under unknown ownership detected", exc); } } catch (ApiException exc) when(exc.StatusCode == HttpStatusCode.NotFound) { throw new ApplicationException($"Organization level projects disabled in {client.Connection.BaseAddress}"); } return project; } async Task EnsurePermissionAsync(Octokit.Team team, Octokit.Project project, string permission) { var inertiaClient = await CreateClientAsync(component, "inertia-preview").ConfigureAwait(false); var url = new Uri($"/orgs/{token.OwnerLogin}/teams/{team.Slug}/projects/{project.Id}", UriKind.Relative); _ = await inertiaClient.Connection .Put <string>(url, new { Permission = permission }) .ConfigureAwait(false); } })); }
async Task <IActionResult> IAdapterAuthorize.HandleCallbackAsync(DeploymentScope deploymentScope, HttpRequest request, IAuthorizationEndpoints authorizationEndpoints) { if (deploymentScope is null) { throw new ArgumentNullException(nameof(deploymentScope)); } if (request is null) { throw new ArgumentNullException(nameof(request)); } if (authorizationEndpoints is null) { throw new ArgumentNullException(nameof(authorizationEndpoints)); } var json = await request .ReadJsonAsync() .ConfigureAwait(false); var appId = json .SelectToken("installation.app_id")? .ToString(); var token = await TokenClient .GetAsync <GitHubToken>(deploymentScope) .ConfigureAwait(false); if (token?.ApplicationId?.Equals(appId, StringComparison.OrdinalIgnoreCase) ?? false) { var action = json .SelectToken("action")? .ToString(); switch (action) { case "created": token.InstallationId = json .SelectToken("installation.id")? .ToString(); break; case "deleted": token = new GitHubToken(deploymentScope); break; case "suspend": token.Suspended = true; break; case "unsuspend": token.Suspended = false; break; default: token = null; break; } if (token is not null) { _ = await TokenClient .SetAsync(token) .ConfigureAwait(false); return(new OkResult()); } } return(new NotFoundResult()); }
async Task <IActionResult> IAdapterAuthorize.HandleAuthorizeAsync(DeploymentScope deploymentScope, HttpRequest request, IAuthorizationEndpoints authorizationEndpoints) { if (deploymentScope is null) { throw new ArgumentNullException(nameof(deploymentScope)); } if (request is null) { throw new ArgumentNullException(nameof(request)); } if (authorizationEndpoints is null) { throw new ArgumentNullException(nameof(authorizationEndpoints)); } var queryParams = Url.ParseQueryParams(request.QueryString.ToString()); var queryState = queryParams.GetValueOrDefault("state"); var queryCode = queryParams.GetValueOrDefault("code"); var queryError = queryParams.GetValueOrDefault("error"); var session = await SessionClient .GetAsync <GitHubSession>(deploymentScope) .ConfigureAwait(false); session ??= await SessionClient .SetAsync(new GitHubSession(deploymentScope)) .ConfigureAwait(false); var data = string.IsNullOrWhiteSpace(deploymentScope.InputData) ? default : TeamCloudSerialize.DeserializeObject <GitHubData>(deploymentScope.InputData); var token = await TokenClient .GetAsync <GitHubToken>(deploymentScope) .ConfigureAwait(false); if (string.IsNullOrEmpty(queryError) && Guid.TryParse(queryState, out var stateId)) { if (!stateId.ToString().Equals(session.SessionId, StringComparison.OrdinalIgnoreCase)) { return(new RedirectResult(authorizationEndpoints.AuthorizationUrl.SetQueryParam("error", "Session timed out.").ToString())); } else if (string.IsNullOrWhiteSpace(queryCode)) { return(new RedirectResult(authorizationEndpoints.AuthorizationUrl.SetQueryParam("error", "Missing GitHub handshake information.").ToString())); } token ??= new GitHubToken(deploymentScope); // Using Flurl as Octokit doesn't support this API yet // https://github.com/octokit/octokit.net/issues/2138 var url = $"https://api.github.com/app-manifests/{queryCode}/conversions"; var response = await url .WithHeader("User-Agent", GitHubConstants.ProductHeader.ToString()) .PostStringAsync(string.Empty) .ConfigureAwait(false); if (!response.IsSuccessStatusCode()) { return(new RedirectResult(authorizationEndpoints.AuthorizationUrl.SetQueryParam("error", $"Failed to get application token ({response.StatusCode} - {response.ResponseMessage.ReasonPhrase}).").ToString())); } var json = await response .GetStringAsync() .ConfigureAwait(false); TeamCloudSerialize.PopulateObject(json, token); if (string.IsNullOrEmpty(token.OwnerLogin)) { token.OwnerLogin = JToken.Parse(json) .SelectToken("owner.login") .ToString(); } token = await TokenClient .SetAsync(token, true) .ConfigureAwait(false); return(new ContentResult { StatusCode = (int)HttpStatusCode.OK, ContentType = "text/html", Content = Assembly.GetExecutingAssembly().GetManifestResourceTemplate($"{GetType().FullName}_Install.html", GetFormContext()) }); } else { return(new ContentResult { StatusCode = (int)HttpStatusCode.OK, ContentType = "text/html", Content = Assembly.GetExecutingAssembly().GetManifestResourceTemplate($"{GetType().FullName}_Register.html", GetFormContext()) }); } object GetFormContext() => new { deploymentScope = deploymentScope, authorizationEndpoints = authorizationEndpoints, token = token, data = data, session = session, error = queryError ?? string.Empty, succeeded = queryParams.Contains("succeeded") }; }