f16ab75e4d32_sshkeys.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. """Migrate SSH keys to the new format.
  2. Revision ID: f16ab75e4d32
  3. Revises: 0a8f99c161e2
  4. Create Date: 2018-09-24 16:11:21.297620
  5. """
  6. # revision identifiers, used by Alembic.
  7. revision = "f16ab75e4d32"
  8. down_revision = "0a8f99c161e2"
  9. import datetime
  10. from alembic import op
  11. import sqlalchemy as sa
  12. from pagure.lib.query import is_valid_ssh_key
  13. def upgrade():
  14. """ Upgrade the database model for the way we store user's public ssh
  15. keys.
  16. For this we leverage the existing ``deploykeys`` table.
  17. It gets renamed to ``sshkeys``, we add the user_id foreign key as now
  18. ssh keys stored in this table can be linked to an user.
  19. Then we convert the existing ssh keys to this database model.
  20. Finally, we drop the ``public_ssh_key`` column from the ``users`` table.
  21. """
  22. users_table = sa.sql.table(
  23. "users",
  24. sa.sql.column("id", sa.Integer),
  25. sa.sql.column("public_ssh_key", sa.TEXT()),
  26. )
  27. sshkey_table = sa.sql.table(
  28. "sshkeys",
  29. sa.sql.column("id", sa.Integer),
  30. sa.sql.column("user_id", sa.Integer),
  31. sa.sql.column("public_ssh_key", sa.TEXT()),
  32. sa.sql.column("ssh_short_key", sa.TEXT()),
  33. sa.sql.column("ssh_search_key", sa.TEXT()),
  34. sa.sql.column("creator_user_id", sa.Integer),
  35. sa.sql.column("pushaccess", sa.Boolean),
  36. sa.sql.column("date_created", sa.DateTime),
  37. )
  38. op.rename_table("deploykeys", "sshkeys")
  39. op.add_column("sshkeys", sa.Column("user_id", sa.Integer(), nullable=True))
  40. op.create_index(
  41. op.f("ix_sshkeys_sshkeys_user_id"),
  42. "sshkeys",
  43. ["user_id"],
  44. unique=False,
  45. )
  46. op.create_foreign_key(
  47. op.f("sshkeys_user_id_fkey"),
  48. "sshkeys",
  49. "users",
  50. ["user_id"],
  51. ["id"],
  52. onupdate=u"CASCADE",
  53. )
  54. print("Convert existing ssh keys to the new format")
  55. seen = []
  56. conn = op.get_bind()
  57. for key in conn.execute(sshkey_table.select()):
  58. ssh_short_key = is_valid_ssh_key(key.public_ssh_key).strip()
  59. ssh_search_key = ssh_short_key.split(" ")[1]
  60. # There is no chance of dupes in the deploykeys alone
  61. seen.append(ssh_search_key)
  62. op.execute(
  63. sshkey_table.update()
  64. .where(sshkey_table.c.id == key.id)
  65. .values({
  66. "ssh_short_key": ssh_short_key,
  67. "ssh_search_key": ssh_search_key,
  68. })
  69. )
  70. data = []
  71. for user in conn.execute(users_table.select()):
  72. if not user.public_ssh_key:
  73. continue
  74. for key in user.public_ssh_key.split("\n"):
  75. if key in (None, False) or not key.strip():
  76. print("Skipping one key")
  77. continue
  78. ssh_short_key = is_valid_ssh_key(key)
  79. if not ssh_short_key:
  80. continue
  81. ssh_short_key = ssh_short_key.strip()
  82. ssh_search_key = ssh_short_key.split(" ")[1]
  83. if ssh_search_key in seen:
  84. print("Skipping previously seen key")
  85. continue
  86. seen.append(ssh_search_key)
  87. print("Key: %s" % key)
  88. print("Short: %s" % ssh_short_key)
  89. print("Search: %s" % ssh_search_key)
  90. tmp = {}
  91. tmp["user_id"] = user.id
  92. tmp["creator_user_id"] = user.id
  93. tmp["public_ssh_key"] = key
  94. tmp["ssh_search_key"] = ssh_search_key
  95. tmp["ssh_short_key"] = ssh_short_key
  96. tmp["pushaccess"] = True
  97. tmp['date_created'] = datetime.datetime.utcnow()
  98. data.append(tmp)
  99. op.bulk_insert(sshkey_table, data)
  100. op.drop_column("users", "public_ssh_key")
  101. def downgrade():
  102. """ Downgrade the database model for the way we store user's public ssh
  103. keys.
  104. For this we bring back the keys present in the ``sshkeys`` table and
  105. put them back into the ``public_ssh_key`` column of the ``users`` table.
  106. """
  107. users_table = sa.sql.table(
  108. "users",
  109. sa.sql.column("id", sa.Integer),
  110. sa.sql.column("public_ssh_key", sa.TEXT()),
  111. )
  112. sshkey_table = sa.sql.table(
  113. "sshkeys",
  114. sa.sql.column("user_id", sa.Integer),
  115. sa.sql.column("public_ssh_key", sa.TEXT()),
  116. sa.sql.column("ssh_short_key", sa.TEXT()),
  117. sa.sql.column("ssh_search_key", sa.TEXT()),
  118. sa.sql.column("creator_user_id", sa.Integer),
  119. sa.sql.column("pushaccess", sa.Boolean),
  120. sa.sql.column("date_created", sa.DateTime),
  121. )
  122. op.add_column(
  123. "users", sa.Column("public_ssh_key", sa.TEXT(), nullable=True)
  124. )
  125. print("Convert existing ssh keys to the old format")
  126. conn = op.get_bind()
  127. data = []
  128. for key in conn.execute(sshkey_table.select()):
  129. if not key.user_id:
  130. continue
  131. user = [
  132. u
  133. for u in conn.execute(
  134. users_table.select().where(users_table.c.id == key.user_id)
  135. )
  136. ]
  137. user = user[0]
  138. ssh_key = ""
  139. if user.public_ssh_key:
  140. ssh_key = user.public_ssh_key + "\n"
  141. ssh_key += key.public_ssh_key
  142. op.execute(
  143. users_table.update()
  144. .where(users_table.c.id == key.user_id)
  145. .values({"public_ssh_key": ssh_key})
  146. )
  147. print("Remove the keys associated with users since we moved them")
  148. op.execute(
  149. sshkey_table.delete()
  150. .where(sshkey_table.c.user_id != None)
  151. )
  152. op.drop_constraint(
  153. op.f("sshkeys_user_id_fkey"), "sshkeys", type_="foreignkey"
  154. )
  155. op.drop_index(op.f("ix_sshkeys_sshkeys_user_id"), table_name="sshkeys")
  156. op.drop_column("sshkeys", "user_id")
  157. op.rename_table("sshkeys", "deploykeys")