Skip to main content

sova_auth/
migration.rs

1//! Fortify AuthMigrator: passport tables + password-reset + RBAC (single init step).
2
3use sea_orm_migration::prelude::*;
4
5/// Compose passport + fortify migrations for `Db::migrations::<AuthMigrator>()`.
6pub struct AuthMigrator;
7
8#[async_trait::async_trait]
9impl MigratorTrait for AuthMigrator {
10    fn migrations() -> Vec<Box<dyn MigrationTrait>> {
11        let mut v = sova_passport::AuthMigrator::migrations();
12        v.push(Box::new(m20260308_000002_fortify::Migration));
13        v
14    }
15}
16
17mod m20260308_000002_fortify {
18    use sea_orm_migration::prelude::*;
19
20    #[derive(DeriveMigrationName)]
21    pub struct Migration;
22
23    #[async_trait::async_trait]
24    impl MigrationTrait for Migration {
25        async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
26            manager
27                .create_table(
28                    Table::create()
29                        .table(AuthPasswordResetTokens::Table)
30                        .if_not_exists()
31                        .col(
32                            ColumnDef::new(AuthPasswordResetTokens::Email)
33                                .string()
34                                .not_null()
35                                .primary_key(),
36                        )
37                        .col(
38                            ColumnDef::new(AuthPasswordResetTokens::TokenHash)
39                                .string()
40                                .not_null(),
41                        )
42                        .col(
43                            ColumnDef::new(AuthPasswordResetTokens::CreatedAt)
44                                .timestamp_with_time_zone()
45                                .not_null(),
46                        )
47                        .to_owned(),
48                )
49                .await?;
50
51            manager
52                .create_table(
53                    Table::create()
54                        .table(AuthRoles::Table)
55                        .if_not_exists()
56                        .col(
57                            ColumnDef::new(AuthRoles::Id)
58                                .big_integer()
59                                .not_null()
60                                .auto_increment()
61                                .primary_key(),
62                        )
63                        .col(ColumnDef::new(AuthRoles::Name).string().not_null())
64                        .col(
65                            ColumnDef::new(AuthRoles::Slug)
66                                .string()
67                                .not_null()
68                                .unique_key(),
69                        )
70                        .to_owned(),
71                )
72                .await?;
73
74            manager
75                .create_table(
76                    Table::create()
77                        .table(AuthPermissions::Table)
78                        .if_not_exists()
79                        .col(
80                            ColumnDef::new(AuthPermissions::Id)
81                                .big_integer()
82                                .not_null()
83                                .auto_increment()
84                                .primary_key(),
85                        )
86                        .col(ColumnDef::new(AuthPermissions::Name).string().not_null())
87                        .col(
88                            ColumnDef::new(AuthPermissions::Slug)
89                                .string()
90                                .not_null()
91                                .unique_key(),
92                        )
93                        .to_owned(),
94                )
95                .await?;
96
97            manager
98                .create_table(
99                    Table::create()
100                        .table(AuthRoleUser::Table)
101                        .if_not_exists()
102                        .col(
103                            ColumnDef::new(AuthRoleUser::UserId)
104                                .big_integer()
105                                .not_null(),
106                        )
107                        .col(
108                            ColumnDef::new(AuthRoleUser::RoleId)
109                                .big_integer()
110                                .not_null(),
111                        )
112                        .primary_key(
113                            Index::create()
114                                .col(AuthRoleUser::UserId)
115                                .col(AuthRoleUser::RoleId),
116                        )
117                        .foreign_key(
118                            ForeignKey::create()
119                                .name("fk_auth_role_user_user")
120                                .from(AuthRoleUser::Table, AuthRoleUser::UserId)
121                                .to(AuthUsers::Table, AuthUsers::Id)
122                                .on_delete(ForeignKeyAction::Cascade),
123                        )
124                        .foreign_key(
125                            ForeignKey::create()
126                                .name("fk_auth_role_user_role")
127                                .from(AuthRoleUser::Table, AuthRoleUser::RoleId)
128                                .to(AuthRoles::Table, AuthRoles::Id)
129                                .on_delete(ForeignKeyAction::Cascade),
130                        )
131                        .to_owned(),
132                )
133                .await?;
134
135            manager
136                .create_index(
137                    Index::create()
138                        .if_not_exists()
139                        .name("idx_auth_role_user_role_id")
140                        .table(AuthRoleUser::Table)
141                        .col(AuthRoleUser::RoleId)
142                        .to_owned(),
143                )
144                .await?;
145
146            manager
147                .create_table(
148                    Table::create()
149                        .table(AuthPermissionRole::Table)
150                        .if_not_exists()
151                        .col(
152                            ColumnDef::new(AuthPermissionRole::RoleId)
153                                .big_integer()
154                                .not_null(),
155                        )
156                        .col(
157                            ColumnDef::new(AuthPermissionRole::PermissionId)
158                                .big_integer()
159                                .not_null(),
160                        )
161                        .primary_key(
162                            Index::create()
163                                .col(AuthPermissionRole::RoleId)
164                                .col(AuthPermissionRole::PermissionId),
165                        )
166                        .foreign_key(
167                            ForeignKey::create()
168                                .name("fk_auth_permission_role_role")
169                                .from(AuthPermissionRole::Table, AuthPermissionRole::RoleId)
170                                .to(AuthRoles::Table, AuthRoles::Id)
171                                .on_delete(ForeignKeyAction::Cascade),
172                        )
173                        .foreign_key(
174                            ForeignKey::create()
175                                .name("fk_auth_permission_role_perm")
176                                .from(
177                                    AuthPermissionRole::Table,
178                                    AuthPermissionRole::PermissionId,
179                                )
180                                .to(AuthPermissions::Table, AuthPermissions::Id)
181                                .on_delete(ForeignKeyAction::Cascade),
182                        )
183                        .to_owned(),
184                )
185                .await?;
186
187            manager
188                .create_index(
189                    Index::create()
190                        .if_not_exists()
191                        .name("idx_auth_permission_role_permission_id")
192                        .table(AuthPermissionRole::Table)
193                        .col(AuthPermissionRole::PermissionId)
194                        .to_owned(),
195                )
196                .await?;
197
198            let conn = manager.get_connection();
199            conn.execute_unprepared(
200                "INSERT INTO auth_roles (name, slug) VALUES ('User', 'user'), ('Admin', 'admin')",
201            )
202            .await?;
203            conn.execute_unprepared(
204                "INSERT INTO auth_permissions (name, slug) VALUES \
205                 ('Cabinet access', 'cabinet.access'), \
206                 ('Manage users', 'users.manage')",
207            )
208            .await?;
209            conn.execute_unprepared(
210                "INSERT INTO auth_permission_role (role_id, permission_id) \
211                 SELECT r.id, p.id FROM auth_roles r, auth_permissions p \
212                 WHERE r.slug = 'user' AND p.slug = 'cabinet.access'",
213            )
214            .await?;
215            conn.execute_unprepared(
216                "INSERT INTO auth_permission_role (role_id, permission_id) \
217                 SELECT r.id, p.id FROM auth_roles r, auth_permissions p \
218                 WHERE r.slug = 'admin'",
219            )
220            .await?;
221
222            Ok(())
223        }
224
225        async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
226            manager
227                .drop_table(Table::drop().table(AuthPermissionRole::Table).to_owned())
228                .await?;
229            manager
230                .drop_table(Table::drop().table(AuthRoleUser::Table).to_owned())
231                .await?;
232            manager
233                .drop_table(Table::drop().table(AuthPermissions::Table).to_owned())
234                .await?;
235            manager
236                .drop_table(Table::drop().table(AuthRoles::Table).to_owned())
237                .await?;
238            manager
239                .drop_table(
240                    Table::drop()
241                        .table(AuthPasswordResetTokens::Table)
242                        .to_owned(),
243                )
244                .await?;
245            Ok(())
246        }
247    }
248
249    #[derive(Iden)]
250    enum AuthUsers {
251        Table,
252        Id,
253    }
254
255    #[derive(Iden)]
256    enum AuthPasswordResetTokens {
257        Table,
258        Email,
259        TokenHash,
260        CreatedAt,
261    }
262
263    #[derive(Iden)]
264    enum AuthRoles {
265        Table,
266        Id,
267        Name,
268        Slug,
269    }
270
271    #[derive(Iden)]
272    enum AuthPermissions {
273        Table,
274        Id,
275        Name,
276        Slug,
277    }
278
279    #[derive(Iden)]
280    enum AuthRoleUser {
281        Table,
282        UserId,
283        RoleId,
284    }
285
286    #[derive(Iden)]
287    enum AuthPermissionRole {
288        Table,
289        RoleId,
290        PermissionId,
291    }
292}