@@ -5,12 +5,9 @@ import (
55 "log/slog"
66 "net/http"
77 "strconv"
8- "strings"
98
109 "github.com/diggerhq/digger/backend/models"
1110 "github.com/diggerhq/digger/backend/segment"
12- "github.com/diggerhq/digger/backend/utils"
13- "github.com/diggerhq/digger/libs/ci/github"
1411 "github.com/gin-gonic/gin"
1512 "github.com/google/uuid"
1613)
@@ -28,29 +25,17 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
2825 c .String (http .StatusBadRequest , "installation_id parameter for github app is empty" )
2926 return
3027 }
31- //setupAction := c.Request.URL.Query()["setup_action"][0]
28+
29+ // Code parameter is optional - GitHub doesn't always send it on re-authorization flows
3230 codeParams , codeExists := c .Request .URL .Query ()["code" ]
33- if ! codeExists || len (codeParams ) == 0 {
34- slog .Error ("There was no code in the url query parameters" )
35- c .String (http .StatusBadRequest , "could not find the code query parameter for github app" )
36- return
37- }
38- code := codeParams [0 ]
39- if len (code ) < 1 {
40- slog .Error ("Code parameter is empty" )
41- c .String (http .StatusBadRequest , "code parameter for github app is empty" )
42- return
31+ code := ""
32+ if codeExists && len (codeParams ) > 0 && len (codeParams [0 ]) > 0 {
33+ code = codeParams [0 ]
4334 }
44- appId := c .Request .URL .Query ().Get ("state" )
4535
46- slog . Info ( "Processing GitHub app callback" , "installationId" , installationId , "appId" , appId )
36+ appId := c . Request . URL . Query (). Get ( "state" )
4737
48- clientId , clientSecret , _ , _ , err := d .GithubClientProvider .FetchCredentials (appId )
49- if err != nil {
50- slog .Error ("Could not fetch credentials for GitHub app" , "appId" , appId , "error" , err )
51- c .String (http .StatusInternalServerError , "could not find credentials for github app" )
52- return
53- }
38+ slog .Info ("Processing GitHub app callback" , "installationId" , installationId , "appId" , appId , "hasCode" , code != "" )
5439
5540 installationId64 , err := strconv .ParseInt (installationId , 10 , 64 )
5641 if err != nil {
@@ -62,38 +47,47 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
6247 return
6348 }
6449
65- slog .Debug ("Validating GitHub callback" , "installationId" , installationId64 , "clientId" , clientId )
50+ // vcsOwner is used for analytics; we'll populate it if we can validate via OAuth
51+ var vcsOwner string
6652
67- result , installation , err := validateGithubCallback ( d . GithubClientProvider , clientId , clientSecret , code , installationId64 )
68- if ! result {
69- slog . Error ( "Failed to validate installation ID" ,
70- "installationId" , installationId64 ,
71- "error" , err ,
72- )
73- c .String (http .StatusInternalServerError , "Failed to validate installation_id. " )
74- return
75- }
53+ // If we have a code parameter, validate the callback via OAuth
54+ // This provides additional security by confirming the user authorized the installation
55+ if code != "" {
56+ clientId , clientSecret , _ , _ , err := d . GithubClientProvider . FetchCredentials ( appId )
57+ if err != nil {
58+ slog . Error ( "Could not fetch credentials for GitHub app" , "appId" , appId , "error" , err )
59+ c .String (http .StatusInternalServerError , "could not find credentials for github app " )
60+ return
61+ }
7662
77- // TODO: Lookup org in GithubAppInstallation by installationID if found use that installationID otherwise
78- // create a new org for this installationID
79- // retrieve org for current orgID
80- installationIdInt64 , err := strconv .ParseInt (installationId , 10 , 64 )
81- if err != nil {
82- slog .Error ("Failed to parse installation ID as int64" ,
83- "installationId" , installationId ,
84- "error" , err ,
63+ slog .Debug ("Validating GitHub callback" , "installationId" , installationId64 , "clientId" , clientId )
64+
65+ result , installation , err := validateGithubCallback (d .GithubClientProvider , clientId , clientSecret , code , installationId64 )
66+ if ! result {
67+ slog .Error ("Failed to validate installation ID" ,
68+ "installationId" , installationId64 ,
69+ "error" , err ,
70+ )
71+ c .String (http .StatusInternalServerError , "Failed to validate installation_id." )
72+ return
73+ }
74+
75+ if installation != nil && installation .Account != nil && installation .Account .Login != nil {
76+ vcsOwner = * installation .Account .Login
77+ }
78+ } else {
79+ slog .Info ("No code parameter provided, skipping OAuth validation (repos will sync via webhook)" ,
80+ "installationId" , installationId64 ,
8581 )
86- c .JSON (http .StatusInternalServerError , gin.H {"error" : "installationId could not be parsed" })
87- return
8882 }
8983
90- slog .Debug ("Looking up GitHub app installation link" , "installationId" , installationIdInt64 )
84+ slog .Debug ("Looking up GitHub app installation link" , "installationId" , installationId64 )
9185
9286 var link * models.GithubAppInstallationLink
93- link , err = models .DB .GetGithubAppInstallationLink (installationIdInt64 )
87+ link , err = models .DB .GetGithubAppInstallationLink (installationId64 )
9488 if err != nil {
9589 slog .Error ("Error getting GitHub app installation link" ,
96- "installationId" , installationIdInt64 ,
90+ "installationId" , installationId64 ,
9791 "error" , err ,
9892 )
9993 c .JSON (http .StatusInternalServerError , gin.H {"error" : "error getting github app link" })
@@ -153,14 +147,10 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
153147 org := link .Organisation
154148 orgId := link .OrganisationId
155149
156- var vcsOwner string = ""
157- if installation .Account .Login != nil {
158- vcsOwner = * installation .Account .Login
159- }
160- // we have multiple repos here, we don't really want to send an track event for each repo, so we just send the vcs owner
150+ // Track the installation event for analytics
161151 segment .Track (* org , vcsOwner , "" , "github" , "vcs_repo_installed" , map [string ]string {})
162152
163- // create a github installation link (org ID matched to installation ID )
153+ // Ensure the installation link exists (idempotent operation )
164154 _ , err = models .DB .CreateGithubInstallationLink (org , installationId64 )
165155 if err != nil {
166156 slog .Error ("Error creating GitHub installation link" ,
@@ -172,120 +162,9 @@ func (d DiggerController) GithubAppCallbackPage(c *gin.Context) {
172162 return
173163 }
174164
175- slog .Debug ("Getting GitHub client" ,
176- "appId" , * installation .AppID ,
177- "installationId" , installationId64 ,
178- )
179-
180- client , _ , err := d .GithubClientProvider .Get (* installation .AppID , installationId64 )
181- if err != nil {
182- slog .Error ("Error retrieving GitHub client" ,
183- "appId" , * installation .AppID ,
184- "installationId" , installationId64 ,
185- "error" , err ,
186- )
187- c .JSON (http .StatusInternalServerError , gin.H {"error" : "Error fetching organisation" })
188- return
189- }
190-
191- // we get repos accessible to this installation
192- slog .Debug ("Listing repositories for installation" , "installationId" , installationId64 )
193-
194- repos , err := github .ListGithubRepos (client )
195- if err != nil {
196- slog .Error ("Failed to list existing repositories" ,
197- "installationId" , installationId64 ,
198- "error" , err ,
199- )
200- c .String (http .StatusInternalServerError , "Failed to list existing repos: %v" , err )
201- return
202- }
203-
204- // resets all existing installations (soft delete)
205- slog .Debug ("Resetting existing GitHub installations" ,
206- "installationId" , installationId ,
207- )
208-
209- var AppInstallation models.GithubAppInstallation
210- err = models .DB .GormDB .Model (& AppInstallation ).Where ("github_installation_id=?" , installationId ).Update ("status" , models .GithubAppInstallDeleted ).Error
211- if err != nil {
212- slog .Error ("Failed to update GitHub installations" ,
213- "installationId" , installationId ,
214- "error" , err ,
215- )
216- c .String (http .StatusInternalServerError , "Failed to update github installations: %v" , err )
217- return
218- }
219-
220- // reset all existing repos (soft delete)
221- slog .Debug ("Soft deleting existing repositories" ,
222- "orgId" , orgId ,
223- )
224-
225- var ExistingRepos []models.Repo
226- err = models .DB .GormDB .Delete (ExistingRepos , "organisation_id=?" , orgId ).Error
227- if err != nil {
228- slog .Error ("Could not delete repositories" ,
229- "orgId" , orgId ,
230- "error" , err ,
231- )
232- c .String (http .StatusInternalServerError , "could not delete repos: %v" , err )
233- return
234- }
235-
236- // here we mark repos that are available one by one
237- slog .Info ("Adding repositories to organization" ,
238- "orgId" , orgId ,
239- "repoCount" , len (repos ),
240- )
241-
242- for i , repo := range repos {
243- repoFullName := * repo .FullName
244- repoOwner := strings .Split (* repo .FullName , "/" )[0 ]
245- repoName := * repo .Name
246- repoUrl := fmt .Sprintf ("https://%v/%v" , utils .GetGithubHostname (), repoFullName )
247-
248- slog .Debug ("Processing repository" ,
249- "index" , i + 1 ,
250- "repoFullName" , repoFullName ,
251- "repoOwner" , repoOwner ,
252- "repoName" , repoName ,
253- )
254-
255- _ , err := models .DB .GithubRepoAdded (
256- installationId64 ,
257- * installation .AppID ,
258- * installation .Account .Login ,
259- * installation .Account .ID ,
260- repoFullName ,
261- )
262- if err != nil {
263- slog .Error ("Error recording GitHub repository" ,
264- "repoFullName" , repoFullName ,
265- "error" , err ,
266- )
267- c .String (http .StatusInternalServerError , "github repos added error: %v" , err )
268- return
269- }
270-
271- cloneUrl := * repo .CloneURL
272- defaultBranch := * repo .DefaultBranch
273-
274- _ , _ , err = createOrGetDiggerRepoForGithubRepo (repoFullName , repoOwner , repoName , repoUrl , installationId64 , * installation .AppID , defaultBranch , cloneUrl )
275- if err != nil {
276- slog .Error ("Error creating or getting Digger repo" ,
277- "repoFullName" , repoFullName ,
278- "error" , err ,
279- )
280- c .String (http .StatusInternalServerError , "createOrGetDiggerRepoForGithubRepo error: %v" , err )
281- return
282- }
283- }
284-
285- slog .Info ("GitHub app callback processed successfully" ,
165+ slog .Info ("GitHub app callback processed" ,
286166 "installationId" , installationId64 ,
287167 "orgId" , orgId ,
288- "repoCount" , len (repos ),
289168 )
290169
291170 c .HTML (http .StatusOK , "github_success.tmpl" , gin.H {})
0 commit comments