gitee.com/zhaochuninhefei/fabric-ca-gm@v0.0.2/lib/server/db/mysql/mysql.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package mysql 8 9 import ( 10 "context" 11 "database/sql" 12 "fmt" 13 "regexp" 14 "strings" 15 16 "gitee.com/zhaochuninhefei/fabric-ca-gm/go-sql-driver/mysql" 17 "gitee.com/zhaochuninhefei/fabric-ca-gm/lib/server/db" 18 "gitee.com/zhaochuninhefei/fabric-ca-gm/lib/server/db/util" 19 "gitee.com/zhaochuninhefei/fabric-ca-gm/lib/tls" 20 "gitee.com/zhaochuninhefei/fabric-gm/bccsp" 21 log "gitee.com/zhaochuninhefei/zcgolog/zclog" 22 "github.com/jmoiron/sqlx" 23 "github.com/pkg/errors" 24 ) 25 26 var ( 27 re = regexp.MustCompile(`\/([0-9,a-z,A-Z$_]+)`) 28 ) 29 30 // Mysql defines MySQL database 31 type Mysql struct { 32 SqlxDB db.FabricCADB 33 TLS *tls.ClientTLSConfig 34 CSP bccsp.BCCSP 35 CAName string 36 Metrics *db.Metrics 37 38 datasource string 39 dbName string 40 } 41 42 // NewDB create a MySQL database 43 func NewDB( 44 datasource, 45 caName string, 46 clientTLSConfig *tls.ClientTLSConfig, 47 csp bccsp.BCCSP, 48 metrics *db.Metrics, 49 ) *Mysql { 50 log.Debugf("Using MySQL database, connecting to database...") 51 return &Mysql{ 52 TLS: clientTLSConfig, 53 CSP: csp, 54 datasource: datasource, 55 CAName: caName, 56 Metrics: metrics, 57 } 58 } 59 60 // Connect connects to a MySQL server 61 func (m *Mysql) Connect() error { 62 datasource := m.datasource 63 clientTLSConfig := m.TLS 64 65 m.dbName = util.GetDBName(datasource) 66 log.Debugf("Database Name: %s", m.dbName) 67 68 connStr := re.ReplaceAllString(datasource, "/") 69 70 if clientTLSConfig.Enabled { 71 tlsConfig, err := tls.GetClientTLSConfig(clientTLSConfig, m.CSP) 72 if err != nil { 73 return errors.WithMessage(err, "Failed to get client TLS for MySQL") 74 } 75 76 mysql.RegisterTLSConfig("custom", tlsConfig) 77 } 78 79 log.Debugf("Connecting to MySQL server, using connection string: %s", util.MaskDBCred(connStr)) 80 sqlxdb, err := sqlx.Connect("mysql", connStr) 81 if err != nil { 82 return errors.Wrap(err, "Failed to connect to MySQL database") 83 } 84 85 m.SqlxDB = db.New(sqlxdb, m.CAName, m.Metrics) 86 return nil 87 } 88 89 // PingContext pings the database 90 func (m *Mysql) PingContext(ctx context.Context) error { 91 err := m.SqlxDB.PingContext(ctx) 92 if err != nil { 93 return errors.Wrap(err, "Failed to ping to MySQL database") 94 } 95 return nil 96 } 97 98 // exists determines if the database has already been created 99 func exists(sqlxDB db.FabricCADB, dbName string) (bool, error) { 100 log.Debugf("Checking if MySQL CA database '%s' exists", dbName) 101 var exists bool 102 query := fmt.Sprintf("SELECT true as 'exists' FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '%s'", dbName) 103 err := sqlxDB.Get("CheckIfDatabaseExists", &exists, query) 104 if err != nil && err != sql.ErrNoRows { 105 return false, errors.Wrapf(err, "Failed to check if MySQL CA database '%s' exists", dbName) 106 } 107 return exists, nil 108 } 109 110 // Create creates database and tables 111 func (m *Mysql) Create() (*db.DB, error) { 112 db, err := m.CreateDatabase() 113 if err != nil { 114 return nil, err 115 } 116 err = m.CreateTables() 117 if err != nil { 118 return nil, err 119 } 120 return db, nil 121 } 122 123 // CreateDatabase creates database 124 func (m *Mysql) CreateDatabase() (*db.DB, error) { 125 err := m.createDatabase() 126 if err != nil { 127 return nil, errors.Wrap(err, "Failed to create MySQL database") 128 } 129 130 log.Debugf("Connecting to database '%s', using connection string: '%s'", m.dbName, util.MaskDBCred(m.datasource)) 131 sqlxdb, err := sqlx.Open("mysql", m.datasource) 132 if err != nil { 133 return nil, errors.Wrapf(err, "Failed to open database '%s' in MySQL server", m.dbName) 134 } 135 m.SqlxDB = db.New(sqlxdb, m.CAName, m.Metrics) 136 137 return m.SqlxDB.(*db.DB), nil 138 } 139 140 // CreateTables creates table 141 func (m *Mysql) CreateTables() error { 142 err := m.createTables() 143 if err != nil { 144 return errors.Wrap(err, "Failed to create MySQL tables") 145 } 146 return nil 147 } 148 149 func (m *Mysql) createDatabase() error { 150 exists, err := exists(m.SqlxDB, m.dbName) 151 if err != nil { 152 return err 153 } 154 if !exists { 155 log.Debugf("Creating MySQL Database '%s'", m.dbName) 156 _, err := m.SqlxDB.Exec("CreateDatabase", "CREATE DATABASE "+m.dbName) 157 if err != nil { 158 return errors.Wrap(err, "Failed to execute create database query") 159 } 160 } 161 return nil 162 } 163 164 func (m *Mysql) createTables() error { 165 db := m.SqlxDB 166 log.Debug("Creating users table if it doesn't exist") 167 if _, err := db.Exec("CreateUsersTable", "CREATE TABLE IF NOT EXISTS users (id VARCHAR(255) NOT NULL, token blob, type VARCHAR(256), affiliation VARCHAR(1024), attributes TEXT, state INTEGER, max_enrollments INTEGER, level INTEGER DEFAULT 0, incorrect_password_attempts INTEGER DEFAULT 0, PRIMARY KEY (id)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 168 return errors.Wrap(err, "Error creating users table") 169 } 170 log.Debug("Creating affiliations table if it doesn't exist") 171 if _, err := db.Exec("CreateAffiliationsTable", "CREATE TABLE IF NOT EXISTS affiliations (id INT NOT NULL AUTO_INCREMENT, name VARCHAR(1024) NOT NULL, prekey VARCHAR(1024), level INTEGER DEFAULT 0, PRIMARY KEY (id)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 172 return errors.Wrap(err, "Error creating affiliations table") 173 } 174 log.Debug("Creating index on 'name' in the affiliations table") 175 if _, err := db.Exec("CreateAffiliationsIndex", "CREATE INDEX name_index on affiliations (name)"); err != nil { 176 if !strings.Contains(err.Error(), "Error 1061") { // Error 1061: Duplicate key name, index already exists 177 return errors.Wrap(err, "Error creating index on affiliations table") 178 } 179 } 180 log.Debug("Creating certificates table if it doesn't exist") 181 if _, err := db.Exec("CreateCertificatesTable", "CREATE TABLE IF NOT EXISTS certificates (id VARCHAR(255), serial_number varbinary(128) NOT NULL, authority_key_identifier varbinary(128) NOT NULL, ca_label varbinary(128), status varbinary(128) NOT NULL, reason int, expiry timestamp DEFAULT 0, revoked_at timestamp DEFAULT 0, pem varbinary(8192) NOT NULL, level INTEGER DEFAULT 0, PRIMARY KEY(serial_number, authority_key_identifier)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 182 return errors.Wrap(err, "Error creating certificates table") 183 } 184 log.Debug("Creating credentials table if it doesn't exist") 185 if _, err := db.Exec("CreateCredentialsTable", "CREATE TABLE IF NOT EXISTS credentials (id VARCHAR(255), revocation_handle varbinary(128) NOT NULL, cred varbinary(4096) NOT NULL, ca_label varbinary(128), status varbinary(128) NOT NULL, reason int, expiry timestamp DEFAULT 0, revoked_at timestamp DEFAULT 0, level INTEGER DEFAULT 0, PRIMARY KEY(revocation_handle)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 186 return errors.Wrap(err, "Error creating credentials table") 187 } 188 log.Debug("Creating revocation_authority_info table if it does not exist") 189 if _, err := db.Exec("CreateRevocationAuthorityTable", "CREATE TABLE IF NOT EXISTS revocation_authority_info (epoch INTEGER, next_handle INTEGER, lasthandle_in_pool INTEGER, level INTEGER DEFAULT 0, PRIMARY KEY (epoch)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 190 return errors.Wrap(err, "Error creating revocation_authority_info table") 191 } 192 log.Debug("Creating nonces table if it does not exist") 193 if _, err := db.Exec("CreateNoncesTable", "CREATE TABLE IF NOT EXISTS nonces (val VARCHAR(255) NOT NULL, expiry timestamp, level INTEGER DEFAULT 0, PRIMARY KEY (val)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 194 return errors.Wrap(err, "Error creating nonces table") 195 } 196 log.Debug("Creating properties table if it does not exist") 197 if _, err := db.Exec("CreatePropertiesTable", "CREATE TABLE IF NOT EXISTS properties (property VARCHAR(255), value VARCHAR(256), PRIMARY KEY(property)) DEFAULT CHARSET=utf8 COLLATE utf8_bin"); err != nil { 198 return errors.Wrap(err, "Error creating properties table") 199 } 200 _, err := db.Exec("CreatePropertiesTable", db.Rebind("INSERT INTO properties (property, value) VALUES ('identity.level', '0'), ('affiliation.level', '0'), ('certificate.level', '0'), ('credential.level', '0'), ('rcinfo.level', '0'), ('nonce.level', '0')")) 201 if err != nil { 202 if !strings.Contains(err.Error(), "1062") { // MySQL error code for duplicate entry 203 return err 204 } 205 } 206 return nil 207 }