code.gitea.io/gitea@v1.21.7/models/migrations/v1_13/v151.go (about)

     1  // Copyright 2020 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package v1_13 //nolint
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"code.gitea.io/gitea/modules/log"
    12  	"code.gitea.io/gitea/modules/setting"
    13  
    14  	"xorm.io/xorm"
    15  	"xorm.io/xorm/schemas"
    16  )
    17  
    18  func SetDefaultPasswordToArgon2(x *xorm.Engine) error {
    19  	switch {
    20  	case setting.Database.Type.IsMySQL():
    21  		_, err := x.Exec("ALTER TABLE `user` ALTER passwd_hash_algo SET DEFAULT 'argon2';")
    22  		return err
    23  	case setting.Database.Type.IsPostgreSQL():
    24  		_, err := x.Exec("ALTER TABLE `user` ALTER COLUMN passwd_hash_algo SET DEFAULT 'argon2';")
    25  		return err
    26  	case setting.Database.Type.IsMSSQL():
    27  		// need to find the constraint and drop it, then recreate it.
    28  		sess := x.NewSession()
    29  		defer sess.Close()
    30  		if err := sess.Begin(); err != nil {
    31  			return err
    32  		}
    33  		res, err := sess.QueryString("SELECT [name] FROM sys.default_constraints WHERE parent_object_id=OBJECT_ID(?) AND COL_NAME(parent_object_id, parent_column_id)=?;", "user", "passwd_hash_algo")
    34  		if err != nil {
    35  			return err
    36  		}
    37  		if len(res) > 0 {
    38  			constraintName := res[0]["name"]
    39  			log.Error("Results of select constraint: %s", constraintName)
    40  			_, err := sess.Exec("ALTER TABLE [user] DROP CONSTRAINT " + constraintName)
    41  			if err != nil {
    42  				return err
    43  			}
    44  			_, err = sess.Exec("ALTER TABLE [user] ADD CONSTRAINT " + constraintName + " DEFAULT 'argon2' FOR passwd_hash_algo")
    45  			if err != nil {
    46  				return err
    47  			}
    48  		} else {
    49  			_, err := sess.Exec("ALTER TABLE [user] ADD DEFAULT('argon2') FOR passwd_hash_algo")
    50  			if err != nil {
    51  				return err
    52  			}
    53  		}
    54  		return sess.Commit()
    55  
    56  	case setting.Database.Type.IsSQLite3():
    57  		// drop through
    58  	default:
    59  		log.Fatal("Unrecognized DB")
    60  	}
    61  
    62  	tables, err := x.DBMetas()
    63  	if err != nil {
    64  		return err
    65  	}
    66  
    67  	// Now for SQLite we have to recreate the table
    68  	var table *schemas.Table
    69  	tableName := "user"
    70  
    71  	for _, table = range tables {
    72  		if table.Name == tableName {
    73  			break
    74  		}
    75  	}
    76  	if table == nil || table.Name != tableName {
    77  		type User struct {
    78  			PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
    79  		}
    80  		return x.Sync(new(User))
    81  	}
    82  	column := table.GetColumn("passwd_hash_algo")
    83  	if column == nil {
    84  		type User struct {
    85  			PasswdHashAlgo string `xorm:"NOT NULL DEFAULT 'argon2'"`
    86  		}
    87  		return x.Sync(new(User))
    88  	}
    89  
    90  	tempTableName := "tmp_recreate__user"
    91  	column.Default = "'argon2'"
    92  
    93  	createTableSQL, _, err := x.Dialect().CreateTableSQL(context.Background(), x.DB(), table, tempTableName)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    98  	sess := x.NewSession()
    99  	defer sess.Close()
   100  	if err := sess.Begin(); err != nil {
   101  		return err
   102  	}
   103  	if _, err := sess.Exec(createTableSQL); err != nil {
   104  		log.Error("Unable to create table %s. Error: %v\n", tempTableName, err, createTableSQL)
   105  		return err
   106  	}
   107  	for _, index := range table.Indexes {
   108  		if _, err := sess.Exec(x.Dialect().CreateIndexSQL(tempTableName, index)); err != nil {
   109  			log.Error("Unable to create indexes on temporary table %s. Error: %v", tempTableName, err)
   110  			return err
   111  		}
   112  	}
   113  
   114  	newTableColumns := table.Columns()
   115  	if len(newTableColumns) == 0 {
   116  		return fmt.Errorf("no columns in new table")
   117  	}
   118  	hasID := false
   119  	for _, column := range newTableColumns {
   120  		hasID = hasID || (column.IsPrimaryKey && column.IsAutoIncrement)
   121  	}
   122  
   123  	sqlStringBuilder := &strings.Builder{}
   124  	_, _ = sqlStringBuilder.WriteString("INSERT INTO `")
   125  	_, _ = sqlStringBuilder.WriteString(tempTableName)
   126  	_, _ = sqlStringBuilder.WriteString("` (`")
   127  	_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
   128  	_, _ = sqlStringBuilder.WriteString("`")
   129  	for _, column := range newTableColumns[1:] {
   130  		_, _ = sqlStringBuilder.WriteString(", `")
   131  		_, _ = sqlStringBuilder.WriteString(column.Name)
   132  		_, _ = sqlStringBuilder.WriteString("`")
   133  	}
   134  	_, _ = sqlStringBuilder.WriteString(")")
   135  	_, _ = sqlStringBuilder.WriteString(" SELECT ")
   136  	if newTableColumns[0].Default != "" {
   137  		_, _ = sqlStringBuilder.WriteString("COALESCE(`")
   138  		_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
   139  		_, _ = sqlStringBuilder.WriteString("`, ")
   140  		_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Default)
   141  		_, _ = sqlStringBuilder.WriteString(")")
   142  	} else {
   143  		_, _ = sqlStringBuilder.WriteString("`")
   144  		_, _ = sqlStringBuilder.WriteString(newTableColumns[0].Name)
   145  		_, _ = sqlStringBuilder.WriteString("`")
   146  	}
   147  
   148  	for _, column := range newTableColumns[1:] {
   149  		if column.Default != "" {
   150  			_, _ = sqlStringBuilder.WriteString(", COALESCE(`")
   151  			_, _ = sqlStringBuilder.WriteString(column.Name)
   152  			_, _ = sqlStringBuilder.WriteString("`, ")
   153  			_, _ = sqlStringBuilder.WriteString(column.Default)
   154  			_, _ = sqlStringBuilder.WriteString(")")
   155  		} else {
   156  			_, _ = sqlStringBuilder.WriteString(", `")
   157  			_, _ = sqlStringBuilder.WriteString(column.Name)
   158  			_, _ = sqlStringBuilder.WriteString("`")
   159  		}
   160  	}
   161  	_, _ = sqlStringBuilder.WriteString(" FROM `")
   162  	_, _ = sqlStringBuilder.WriteString(tableName)
   163  	_, _ = sqlStringBuilder.WriteString("`")
   164  
   165  	if _, err := sess.Exec(sqlStringBuilder.String()); err != nil {
   166  		log.Error("Unable to set copy data in to temp table %s. Error: %v", tempTableName, err)
   167  		return err
   168  	}
   169  
   170  	// SQLite will drop all the constraints on the old table
   171  	if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
   172  		log.Error("Unable to drop old table %s. Error: %v", tableName, err)
   173  		return err
   174  	}
   175  
   176  	for _, index := range table.Indexes {
   177  		if _, err := sess.Exec(x.Dialect().DropIndexSQL(tempTableName, index)); err != nil {
   178  			log.Error("Unable to drop indexes on temporary table %s. Error: %v", tempTableName, err)
   179  			return err
   180  		}
   181  	}
   182  
   183  	if _, err := sess.Exec(fmt.Sprintf("ALTER TABLE `%s` RENAME TO `%s`", tempTableName, tableName)); err != nil {
   184  		log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
   185  		return err
   186  	}
   187  
   188  	for _, index := range table.Indexes {
   189  		if _, err := sess.Exec(x.Dialect().CreateIndexSQL(tableName, index)); err != nil {
   190  			log.Error("Unable to recreate indexes on table %s. Error: %v", tableName, err)
   191  			return err
   192  		}
   193  	}
   194  
   195  	return sess.Commit()
   196  }