github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/migration/migrations/1528470872_add_global_users.up.go (about) 1 package migrations 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "strings" 9 10 "github.com/mitchellh/mapstructure" 11 ) 12 13 func (self *migrations) Up_1528470872() error { 14 15 type team struct { 16 id int64 17 name string 18 auth []byte 19 nonce sql.NullString 20 } 21 22 tx, err := self.DB.Begin() 23 if err != nil { 24 return err 25 } 26 27 _, err = tx.Exec("ALTER TABLE teams RENAME COLUMN auth TO legacy_auth") 28 if err != nil { 29 tx.Rollback() 30 return err 31 } 32 33 _, err = tx.Exec("ALTER TABLE teams ADD COLUMN auth text") 34 if err != nil { 35 tx.Rollback() 36 return err 37 } 38 39 rows, err := tx.Query("SELECT id, name, legacy_auth, nonce FROM teams") 40 if err != nil { 41 tx.Rollback() 42 return err 43 } 44 45 teams := []team{} 46 47 for rows.Next() { 48 team := team{} 49 50 if err = rows.Scan(&team.id, &team.name, &team.auth, &team.nonce); err != nil { 51 tx.Rollback() 52 return err 53 } 54 55 teams = append(teams, team) 56 } 57 58 mustBeUniqueAmongstAllTeams := map[string]map[string]map[string][]string{ 59 "basicauth": map[string]map[string][]string{ 60 "username": map[string][]string{}, 61 }, 62 } 63 64 mustBeSameAmongstAllTeams := map[string]map[string]map[string][]string{ 65 "github": map[string]map[string][]string{ 66 "auth_url": map[string][]string{}, 67 "token_url": map[string][]string{}, 68 "api_url": map[string][]string{}, 69 }, 70 "uaa": map[string]map[string][]string{ 71 "auth_url": map[string][]string{}, 72 "token_url": map[string][]string{}, 73 "cf_url": map[string][]string{}, 74 }, 75 "gitlab": map[string]map[string][]string{ 76 "auth_url": map[string][]string{}, 77 "token_url": map[string][]string{}, 78 "api_url": map[string][]string{}, 79 }, 80 "oauth": map[string]map[string][]string{ 81 "auth_url": map[string][]string{}, 82 "token_url": map[string][]string{}, 83 }, 84 "oauth_oidc": map[string]map[string][]string{ 85 "auth_url": map[string][]string{}, 86 "token_url": map[string][]string{}, 87 }, 88 } 89 90 for _, team := range teams { 91 92 var noncense *string 93 if team.nonce.Valid { 94 noncense = &team.nonce.String 95 } 96 97 decryptedAuth, err := self.Strategy.Decrypt(string(team.auth), noncense) 98 if err != nil { 99 tx.Rollback() 100 return err 101 } 102 103 var authConfig map[string]interface{} 104 if err = json.Unmarshal(decryptedAuth, &authConfig); err != nil { 105 tx.Rollback() 106 return err 107 } 108 109 if authConfig == nil { 110 authConfig = map[string]interface{}{} 111 } 112 113 newGroups := []string{} 114 newUsers := []string{} 115 116 for provider, rawConfig := range authConfig { 117 118 for key, set := range mustBeSameAmongstAllTeams[provider] { 119 if parsedConfig, ok := rawConfig.(map[string]interface{}); ok { 120 if value, ok := parsedConfig[key].(string); ok { 121 _, valuePresent := set[value] 122 if valuePresent { 123 set[value] = append(set[value], team.name) 124 } else { 125 set[value] = []string{team.name} 126 } 127 } 128 } 129 } 130 131 for key, set := range mustBeUniqueAmongstAllTeams[provider] { 132 if parsedConfig, ok := rawConfig.(map[string]interface{}); ok { 133 if value, parseOk := parsedConfig[key].(string); parseOk { 134 _, valuePresent := set[value] 135 if valuePresent { 136 set[value] = append(set[value], team.name) 137 } else { 138 set[value] = []string{team.name} 139 } 140 } 141 } 142 } 143 144 switch provider { 145 case "github": 146 var config struct { 147 Organizations []string `mapstructure:"organizations"` 148 Teams []struct { 149 OrganizationName string `mapstructure:"organization_name"` 150 TeamName string `mapstructure:"team_name"` 151 } `mapstructure:"teams"` 152 Users []string `mapstructure:"users"` 153 } 154 if err = mapstructure.Decode(rawConfig, &config); err != nil { 155 tx.Rollback() 156 return err 157 } 158 159 for _, team := range config.Teams { 160 newGroups = append(newGroups, provider+":"+team.OrganizationName+":"+team.TeamName) 161 } 162 for _, org := range config.Organizations { 163 newGroups = append(newGroups, provider+":"+org) 164 } 165 for _, user := range config.Users { 166 newUsers = append(newUsers, provider+":"+user) 167 } 168 169 case "basicauth": 170 var config struct { 171 Username string `mapstructure:"username"` 172 } 173 if err = mapstructure.Decode(rawConfig, &config); err != nil { 174 tx.Rollback() 175 return err 176 } 177 178 newUsers = append(newUsers, "local:"+config.Username) 179 180 case "uaa": 181 var config struct { 182 Spaces []string `mapstructure:"cf_spaces"` 183 } 184 if err = mapstructure.Decode(rawConfig, &config); err != nil { 185 tx.Rollback() 186 return err 187 } 188 189 for _, space := range config.Spaces { 190 newGroups = append(newGroups, "cf:"+space) 191 } 192 193 case "gitlab": 194 var config struct { 195 Groups []string `mapstructure:"groups"` 196 } 197 if err = mapstructure.Decode(rawConfig, &config); err != nil { 198 tx.Rollback() 199 return err 200 } 201 202 for _, group := range config.Groups { 203 newGroups = append(newGroups, "gitlab:"+group) 204 } 205 206 case "oauth": 207 var config struct { 208 Scope string `mapstructure:"scope"` 209 } 210 if err = mapstructure.Decode(rawConfig, &config); err != nil { 211 tx.Rollback() 212 return err 213 } 214 215 newGroups = append(newGroups, "oauth:"+config.Scope) 216 217 case "oauth_oidc": 218 var config struct { 219 UserID []string `mapstructure:"user_id"` 220 Groups []string `mapstructure:"groups"` 221 } 222 if err = mapstructure.Decode(rawConfig, &config); err != nil { 223 tx.Rollback() 224 return err 225 } 226 227 for _, user := range config.UserID { 228 newUsers = append(newUsers, "oidc:"+user) 229 } 230 for _, group := range config.Groups { 231 newGroups = append(newGroups, "oidc:"+group) 232 } 233 234 case "bitbucket-server", "bitbucket-cloud": 235 tx.Rollback() 236 return errors.New("Bitbucket is no longer supported") 237 } 238 } 239 240 newAuth, err := json.Marshal(map[string][]string{ 241 "users": newUsers, 242 "groups": newGroups, 243 }) 244 if err != nil { 245 tx.Rollback() 246 return err 247 } 248 249 _, err = tx.Exec("UPDATE teams SET auth = $1 WHERE id = $2", newAuth, team.id) 250 if err != nil { 251 tx.Rollback() 252 return err 253 } 254 } 255 256 errorMessage := "" 257 for provider, keys := range mustBeSameAmongstAllTeams { 258 for key, values := range keys { 259 if len(values) > 1 { 260 errorMessage += fmt.Sprintf("Non-unique value of '%s' for auth provider '%s' breaks migration: ", key, provider) 261 offendingTeams := []string{} 262 for value, teams := range values { 263 offendingTeams = append(offendingTeams, fmt.Sprintf("teams %v have value '%s'", teams, value)) 264 } 265 errorMessage += strings.Join(offendingTeams, ", ") 266 errorMessage += "\n" 267 } 268 } 269 } 270 for provider, keys := range mustBeUniqueAmongstAllTeams { 271 for key, values := range keys { 272 for value, teams := range values { 273 if len(teams) > 1 { 274 errorMessage += fmt.Sprintf("Multiple teams having the same value, '%s', of '%s' for auth provider '%s' breaks migration. Offending teams: %v\n", value, key, provider, teams) 275 } 276 } 277 } 278 } 279 if errorMessage != "" { 280 tx.Rollback() 281 return fmt.Errorf("Problems in your database caused the migration to fail:\n\n%s", errorMessage) 282 } 283 284 err = tx.Commit() 285 if err != nil { 286 tx.Rollback() 287 return err 288 } 289 290 return nil 291 }