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