yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/qcloud/rds_mysql_account.go (about) 1 // Copyright 2019 Yunion 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package qcloud 16 17 import ( 18 "fmt" 19 "strings" 20 21 "gopkg.in/fatih/set.v0" 22 23 "yunion.io/x/pkg/errors" 24 "yunion.io/x/pkg/utils" 25 26 api "yunion.io/x/cloudmux/pkg/apis/compute" 27 "yunion.io/x/cloudmux/pkg/cloudprovider" 28 "yunion.io/x/cloudmux/pkg/multicloud" 29 ) 30 31 type SMySQLInstanceAccount struct { 32 multicloud.SDBInstanceAccountBase 33 QcloudTags 34 rds *SMySQLInstance 35 36 Notes string 37 Host string 38 User string 39 ModifyTime string 40 ModifyPasswordTime string 41 CreateTime string 42 } 43 44 func (self *SMySQLInstanceAccount) GetName() string { 45 return self.User 46 } 47 48 func (self *SMySQLInstanceAccount) GetHost() string { 49 return self.Host 50 } 51 52 func (self *SMySQLInstanceAccount) ResetPassword(password string) error { 53 return self.rds.region.ModifyMySQLAccountPassword(self.rds.InstanceId, password, map[string]string{self.User: self.Host}) 54 } 55 56 func (self *SMySQLInstanceAccount) Delete() error { 57 return self.rds.region.DeleteMySQLAccounts(self.rds.InstanceId, map[string]string{self.User: self.Host}) 58 } 59 60 type sPrivilege struct { 61 Database string 62 Privilege string 63 User string 64 Host string 65 } 66 67 func (p sPrivilege) GetGlobalId() string { 68 return fmt.Sprintf("%s-%s-%s-%s", p.User, p.Host, p.Database, p.Privilege) 69 } 70 71 func (p sPrivilege) GetDBName() string { 72 return p.Database 73 } 74 75 func (p sPrivilege) GetPrivilege() string { 76 return p.Privilege 77 } 78 79 func (self *SRegion) GrantAccountPrivilege(instanceId, user, host, database, privilege string) error { 80 privileges := []string{} 81 switch privilege { 82 case api.DATABASE_PRIVILEGE_RW: 83 privileges = api.QCLOUD_RW_PRIVILEGE_SET 84 case api.DATABASE_PRIVILEGE_R: 85 privileges = api.QCLOUD_R_PRIVILEGE_SET 86 default: 87 return fmt.Errorf("unknow privilege %s", privilege) 88 } 89 priv, err := self.DescribeAccountPrivileges(instanceId, user, host) 90 if err != nil { 91 return errors.Wrapf(err, "DescribeAccountPrivileges") 92 } 93 params := map[string]string{ 94 "InstanceId": instanceId, 95 "Accounts.0.User": user, 96 "Accounts.0.Host": host, 97 } 98 for i, p := range priv.GlobalPrivileges { 99 params[fmt.Sprintf("GlobalPrivileges.%d", i)] = p 100 } 101 find := false 102 for i, p := range priv.DatabasePrivileges { 103 params[fmt.Sprintf("DatabasePrivileges.%d.Database", i)] = p.Database 104 if database == p.Database { 105 p.Privileges = privileges 106 find = true 107 } 108 for j, v := range p.Privileges { 109 params[fmt.Sprintf("DatabasePrivileges.%d.Privileges.%d", i, j)] = v 110 } 111 } 112 if !find { 113 params[fmt.Sprintf("DatabasePrivileges.%d.Database", len(priv.DatabasePrivileges))] = database 114 for j, v := range privileges { 115 params[fmt.Sprintf("DatabasePrivileges.%d.Privileges.%d", len(priv.DatabasePrivileges), j)] = v 116 } 117 } 118 for i, p := range priv.TablePrivileges { 119 params[fmt.Sprintf("TablePrivileges.%d.Database", i)] = p.Database 120 params[fmt.Sprintf("TablePrivileges.%d.Table", i)] = p.Table 121 for j, v := range p.Privileges { 122 params[fmt.Sprintf("TablePrivileges.%d.Privileges.%d", i, j)] = v 123 } 124 } 125 for i, p := range priv.ColumnPrivileges { 126 params[fmt.Sprintf("ColumnPrivileges.%d.Database", i)] = p.Database 127 params[fmt.Sprintf("ColumnPrivileges.%d.Table", i)] = p.Table 128 params[fmt.Sprintf("ColumnPrivileges.%d.Column", i)] = p.Column 129 for j, v := range p.Privileges { 130 params[fmt.Sprintf("ColumnPrivileges.%d.Privileges.%d", i, j)] = v 131 } 132 } 133 resp, err := self.cdbRequest("ModifyAccountPrivileges", params) 134 if err != nil { 135 return errors.Wrapf(err, "ModifyAccountPrivileges") 136 } 137 asyncRequestId, _ := resp.GetString("AsyncRequestId") 138 return self.waitAsyncAction("ModifyAccountPrivileges", instanceId, asyncRequestId) 139 } 140 141 func (self *SRegion) RevokeAccountPrivilege(instanceId, user, host, database string) error { 142 priv, err := self.DescribeAccountPrivileges(instanceId, user, host) 143 if err != nil { 144 return errors.Wrapf(err, "DescribeAccountPrivileges") 145 } 146 params := map[string]string{ 147 "InstanceId": instanceId, 148 "Accounts.0.User": user, 149 "Accounts.0.Host": host, 150 } 151 for i, p := range priv.GlobalPrivileges { 152 params[fmt.Sprintf("GlobalPrivileges.%d", i)] = p 153 } 154 idx := 0 155 for _, p := range priv.DatabasePrivileges { 156 if p.Database == database { 157 continue 158 } 159 params[fmt.Sprintf("DatabasePrivileges.%d.Database", idx)] = p.Database 160 for i, v := range p.Privileges { 161 params[fmt.Sprintf("DatabasePrivileges.%d.Privileges.%d", idx, i)] = v 162 } 163 } 164 for i, p := range priv.TablePrivileges { 165 params[fmt.Sprintf("TablePrivileges.%d.Database", i)] = p.Database 166 params[fmt.Sprintf("TablePrivileges.%d.Table", i)] = p.Table 167 for j, v := range p.Privileges { 168 params[fmt.Sprintf("TablePrivileges.%d.Privileges.%d", i, j)] = v 169 } 170 } 171 for i, p := range priv.ColumnPrivileges { 172 params[fmt.Sprintf("ColumnPrivileges.%d.Database", i)] = p.Database 173 params[fmt.Sprintf("ColumnPrivileges.%d.Table", i)] = p.Table 174 params[fmt.Sprintf("ColumnPrivileges.%d.Column", i)] = p.Column 175 for j, v := range p.Privileges { 176 params[fmt.Sprintf("ColumnPrivileges.%d.Privileges.%d", i, j)] = v 177 } 178 } 179 resp, err := self.cdbRequest("ModifyAccountPrivileges", params) 180 if err != nil { 181 return errors.Wrapf(err, "ModifyAccountPrivileges") 182 } 183 asyncRequestId, _ := resp.GetString("AsyncRequestId") 184 return self.waitAsyncAction("ModifyAccountPrivileges", instanceId, asyncRequestId) 185 } 186 187 func (self *SMySQLInstanceAccount) GrantPrivilege(database, privilege string) error { 188 return self.rds.region.GrantAccountPrivilege(self.rds.InstanceId, self.User, self.Host, database, privilege) 189 } 190 191 func (self *SMySQLInstanceAccount) RevokePrivilege(database string) error { 192 return self.rds.region.RevokeAccountPrivilege(self.rds.InstanceId, self.User, self.Host, database) 193 } 194 195 func (self *SMySQLInstanceAccount) GetIDBInstanceAccountPrivileges() ([]cloudprovider.ICloudDBInstanceAccountPrivilege, error) { 196 if utils.IsInStringArray(self.User, []string{"mysql.infoschema", "mysql.session", "mysql.sys"}) { 197 return []cloudprovider.ICloudDBInstanceAccountPrivilege{}, nil 198 } 199 priv, err := self.rds.region.DescribeAccountPrivileges(self.rds.InstanceId, self.User, self.Host) 200 if err != nil { 201 return nil, errors.Wrapf(err, "DescribeAccountPrivileges") 202 } 203 ret := []cloudprovider.ICloudDBInstanceAccountPrivilege{} 204 rwSet := set.New(set.ThreadSafe) 205 for _, p := range api.QCLOUD_RW_PRIVILEGE_SET { 206 rwSet.Add(p) 207 } 208 rSet := set.New(set.ThreadSafe) 209 for _, p := range api.QCLOUD_R_PRIVILEGE_SET { 210 rSet.Add(p) 211 } 212 for _, p := range priv.DatabasePrivileges { 213 pSet := set.New(set.ThreadSafe) 214 for _, v := range p.Privileges { 215 pSet.Add(v) 216 } 217 priv := strings.Join(p.Privileges, ",") 218 if pSet.IsEqual(rSet) { 219 priv = api.DATABASE_PRIVILEGE_R 220 } else if pSet.IsEqual(rwSet) { 221 priv = api.DATABASE_PRIVILEGE_RW 222 } 223 privilege := &sPrivilege{ 224 Database: p.Database, 225 User: self.User, 226 Host: self.Host, 227 Privilege: priv, 228 } 229 ret = append(ret, privilege) 230 } 231 return ret, nil 232 } 233 234 func (self *SRegion) ModifyMySQLAccountPassword(instanceId string, password string, users map[string]string) error { 235 params := map[string]string{ 236 "InstanceId": instanceId, 237 "NewPassword": password, 238 } 239 idx := 0 240 for user, host := range users { 241 params[fmt.Sprintf("Accounts.%d.user", idx)] = user 242 params[fmt.Sprintf("Accounts.%d.host", idx)] = host 243 idx++ 244 } 245 resp, err := self.cdbRequest("ModifyAccountPassword", params) 246 if err != nil { 247 return errors.Wrapf(err, "ModifyAccountPassword") 248 } 249 asyncRequestId, _ := resp.GetString("AsyncRequestId") 250 return self.waitAsyncAction("ModifyAccountPassword", instanceId, asyncRequestId) 251 } 252 253 func (self *SRegion) DeleteMySQLAccounts(instanceId string, users map[string]string) error { 254 params := map[string]string{ 255 "InstanceId": instanceId, 256 } 257 idx := 0 258 for user, host := range users { 259 params[fmt.Sprintf("Accounts.%d.user", idx)] = user 260 params[fmt.Sprintf("Accounts.%d.host", idx)] = host 261 idx++ 262 } 263 resp, err := self.cdbRequest("DeleteAccounts", params) 264 if err != nil { 265 return errors.Wrapf(err, "DeleteAccounts") 266 } 267 asyncRequestId, _ := resp.GetString("AsyncRequestId") 268 return self.waitAsyncAction("DeleteAccounts", instanceId, asyncRequestId) 269 } 270 271 func (self *SRegion) DescribeMySQLAccounts(instanceId string, offset, limit int) ([]SMySQLInstanceAccount, int, error) { 272 if limit < 1 || limit > 100 { 273 limit = 100 274 } 275 params := map[string]string{ 276 "InstanceId": instanceId, 277 "Offset": fmt.Sprintf("%d", offset), 278 "Limit": fmt.Sprintf("%d", limit), 279 } 280 resp, err := self.cdbRequest("DescribeAccounts", params) 281 if err != nil { 282 return nil, 0, errors.Wrapf(err, "DescribeAccounts") 283 } 284 ret := []SMySQLInstanceAccount{} 285 err = resp.Unmarshal(&ret, "Items") 286 if err != nil { 287 return nil, 0, errors.Wrapf(err, "resp.Unmarshal") 288 } 289 totalCount, _ := resp.Float("TotalCount") 290 return ret, int(totalCount), nil 291 } 292 293 func (self *SMySQLInstance) GetIDBInstanceAccounts() ([]cloudprovider.ICloudDBInstanceAccount, error) { 294 accounts := []SMySQLInstanceAccount{} 295 for { 296 part, total, err := self.region.DescribeMySQLAccounts(self.InstanceId, len(accounts), 100) 297 if err != nil { 298 return nil, errors.Wrapf(err, "DescribeMySQLAccounts") 299 } 300 accounts = append(accounts, part...) 301 if len(accounts) >= total { 302 break 303 } 304 } 305 ret := []cloudprovider.ICloudDBInstanceAccount{} 306 for i := range accounts { 307 if len(accounts[i].User) > 0 { // 忽略用户为空的用户 308 accounts[i].rds = self 309 ret = append(ret, &accounts[i]) 310 } 311 } 312 return ret, nil 313 } 314 315 func (self *SRegion) CreateMySQLAccount(instanceId string, opts *cloudprovider.SDBInstanceAccountCreateConfig) error { 316 params := map[string]string{ 317 "InstanceId": instanceId, 318 "Password": opts.Password, 319 "Accounts.0.User": opts.Name, 320 "Accounts.0.Host": opts.Host, 321 "Description": opts.Description, 322 } 323 resp, err := self.cdbRequest("CreateAccounts", params) 324 if err != nil { 325 return errors.Wrapf(err, "CreateAccounts") 326 } 327 asyncRequestId, _ := resp.GetString("AsyncRequestId") 328 return self.waitAsyncAction("CreateAccounts", instanceId, asyncRequestId) 329 } 330 331 type SDatabasePrivilege struct { 332 Privileges []string 333 Database string 334 } 335 336 type STablePrivilege struct { 337 Database string 338 Table string 339 Privileges []string 340 } 341 342 type SColumnPrivilege struct { 343 Database string 344 Table string 345 Column string 346 Privileges []string 347 } 348 349 type SAccountPrivilege struct { 350 GlobalPrivileges []string 351 DatabasePrivileges []SDatabasePrivilege 352 TablePrivileges []STablePrivilege 353 ColumnPrivileges []SColumnPrivilege 354 } 355 356 func (self *SRegion) DescribeAccountPrivileges(instanceId string, user, host string) (*SAccountPrivilege, error) { 357 params := map[string]string{ 358 "InstanceId": instanceId, 359 "User": user, 360 "Host": host, 361 } 362 resp, err := self.cdbRequest("DescribeAccountPrivileges", params) 363 if err != nil { 364 return nil, errors.Wrapf(err, "DescribeAccountPrivileges") 365 } 366 priv := &SAccountPrivilege{} 367 err = resp.Unmarshal(priv) 368 if err != nil { 369 return nil, errors.Wrapf(err, "resp.Unmarshal") 370 } 371 return priv, nil 372 }