Exemple #1
0
    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);
    }
Exemple #2
0
    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));
    }
Exemple #3
0
    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);
            }
        }
Exemple #5
0
        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);
            }
        }
Exemple #6
0
    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);
            }
        }));
    }
Exemple #7
0
    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());
    }
Exemple #8
0
    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")
        };
    }