code.gitea.io/gitea@v1.21.7/models/migrations/v1_19/v233.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package v1_19 //nolint 5 6 import ( 7 "fmt" 8 9 "code.gitea.io/gitea/modules/json" 10 "code.gitea.io/gitea/modules/secret" 11 "code.gitea.io/gitea/modules/setting" 12 api "code.gitea.io/gitea/modules/structs" 13 14 "xorm.io/builder" 15 "xorm.io/xorm" 16 ) 17 18 func batchProcess[T any](x *xorm.Engine, buf []T, query func(limit, start int) *xorm.Session, process func(*xorm.Session, T) error) error { 19 size := cap(buf) 20 start := 0 21 for { 22 err := query(size, start).Find(&buf) 23 if err != nil { 24 return err 25 } 26 if len(buf) == 0 { 27 return nil 28 } 29 30 err = func() error { 31 sess := x.NewSession() 32 defer sess.Close() 33 if err := sess.Begin(); err != nil { 34 return fmt.Errorf("unable to allow start session. Error: %w", err) 35 } 36 for _, record := range buf { 37 if err := process(sess, record); err != nil { 38 return err 39 } 40 } 41 return sess.Commit() 42 }() 43 if err != nil { 44 return err 45 } 46 47 if len(buf) < size { 48 return nil 49 } 50 start += size 51 buf = buf[:0] 52 } 53 } 54 55 func AddHeaderAuthorizationEncryptedColWebhook(x *xorm.Engine) error { 56 // Add the column to the table 57 type Webhook struct { 58 ID int64 `xorm:"pk autoincr"` 59 Type string `xorm:"VARCHAR(16) 'type'"` 60 Meta string `xorm:"TEXT"` // store hook-specific attributes 61 62 // HeaderAuthorizationEncrypted should be accessed using HeaderAuthorization() and SetHeaderAuthorization() 63 HeaderAuthorizationEncrypted string `xorm:"TEXT"` 64 } 65 err := x.Sync(new(Webhook)) 66 if err != nil { 67 return err 68 } 69 70 // Migrate the matrix webhooks 71 72 type MatrixMeta struct { 73 HomeserverURL string `json:"homeserver_url"` 74 Room string `json:"room_id"` 75 MessageType int `json:"message_type"` 76 } 77 type MatrixMetaWithAccessToken struct { 78 MatrixMeta 79 AccessToken string `json:"access_token"` 80 } 81 82 err = batchProcess(x, 83 make([]*Webhook, 0, 50), 84 func(limit, start int) *xorm.Session { 85 return x.Where("type=?", "matrix").OrderBy("id").Limit(limit, start) 86 }, 87 func(sess *xorm.Session, hook *Webhook) error { 88 // retrieve token from meta 89 var withToken MatrixMetaWithAccessToken 90 err := json.Unmarshal([]byte(hook.Meta), &withToken) 91 if err != nil { 92 return fmt.Errorf("unable to unmarshal matrix meta for webhook[id=%d]: %w", hook.ID, err) 93 } 94 if withToken.AccessToken == "" { 95 return nil 96 } 97 98 // encrypt token 99 authorization := "Bearer " + withToken.AccessToken 100 hook.HeaderAuthorizationEncrypted, err = secret.EncryptSecret(setting.SecretKey, authorization) 101 if err != nil { 102 return fmt.Errorf("unable to encrypt access token for webhook[id=%d]: %w", hook.ID, err) 103 } 104 105 // remove token from meta 106 withoutToken, err := json.Marshal(withToken.MatrixMeta) 107 if err != nil { 108 return fmt.Errorf("unable to marshal matrix meta for webhook[id=%d]: %w", hook.ID, err) 109 } 110 hook.Meta = string(withoutToken) 111 112 // save in database 113 count, err := sess.ID(hook.ID).Cols("meta", "header_authorization_encrypted").Update(hook) 114 if count != 1 || err != nil { 115 return fmt.Errorf("unable to update header_authorization_encrypted for webhook[id=%d]: %d,%w", hook.ID, count, err) 116 } 117 return nil 118 }) 119 if err != nil { 120 return err 121 } 122 123 // Remove access_token from HookTask 124 125 type HookTask struct { 126 ID int64 `xorm:"pk autoincr"` 127 HookID int64 128 PayloadContent string `xorm:"LONGTEXT"` 129 } 130 131 type MatrixPayloadSafe struct { 132 Body string `json:"body"` 133 MsgType string `json:"msgtype"` 134 Format string `json:"format"` 135 FormattedBody string `json:"formatted_body"` 136 Commits []*api.PayloadCommit `json:"io.gitea.commits,omitempty"` 137 } 138 type MatrixPayloadUnsafe struct { 139 MatrixPayloadSafe 140 AccessToken string `json:"access_token"` 141 } 142 143 err = batchProcess(x, 144 make([]*HookTask, 0, 50), 145 func(limit, start int) *xorm.Session { 146 return x.Where(builder.And( 147 builder.In("hook_id", builder.Select("id").From("webhook").Where(builder.Eq{"type": "matrix"})), 148 builder.Like{"payload_content", "access_token"}, 149 )).OrderBy("id").Limit(limit, 0) // ignore the provided "start", since other payload were already converted and don't contain 'payload_content' anymore 150 }, 151 func(sess *xorm.Session, hookTask *HookTask) error { 152 // retrieve token from payload_content 153 var withToken MatrixPayloadUnsafe 154 err := json.Unmarshal([]byte(hookTask.PayloadContent), &withToken) 155 if err != nil { 156 return fmt.Errorf("unable to unmarshal payload_content for hook_task[id=%d]: %w", hookTask.ID, err) 157 } 158 if withToken.AccessToken == "" { 159 return nil 160 } 161 162 // remove token from payload_content 163 withoutToken, err := json.Marshal(withToken.MatrixPayloadSafe) 164 if err != nil { 165 return fmt.Errorf("unable to marshal payload_content for hook_task[id=%d]: %w", hookTask.ID, err) 166 } 167 hookTask.PayloadContent = string(withoutToken) 168 169 // save in database 170 count, err := sess.ID(hookTask.ID).Cols("payload_content").Update(hookTask) 171 if count != 1 || err != nil { 172 return fmt.Errorf("unable to update payload_content for hook_task[id=%d]: %d,%w", hookTask.ID, count, err) 173 } 174 return nil 175 }) 176 if err != nil { 177 return err 178 } 179 180 return nil 181 }