[SECURITY][DB] Make user register 'atomic', by using a single transaction for inserting all objects, to avoid partial inserts

This commit is contained in:
Hugo Sales 2021-04-23 12:54:25 +00:00
parent 1bad2fa050
commit b82658e345
2 changed files with 26 additions and 11 deletions

View File

@ -93,17 +93,19 @@ class Security extends Controller
try { try {
$actor = GSActor::create(['nickname' => $data['nickname']]); $actor = GSActor::create(['nickname' => $data['nickname']]);
DB::persist($actor);
DB::flush();
$id = $actor->getId();
$user = LocalUser::create([ $user = LocalUser::create([
'id' => $id,
'nickname' => $data['nickname'], 'nickname' => $data['nickname'],
'outgoing_email' => $data['email'], 'outgoing_email' => $data['email'],
'incoming_email' => $data['email'], 'incoming_email' => $data['email'],
'password' => LocalUser::hashPassword($data['password']), 'password' => LocalUser::hashPassword($data['password']),
]); ]);
DB::persist($user); DB::persistWithSameId(
$actor,
$user,
// Self follow
fn (int $id) => DB::persist(Follow::create(['follower' => $id, 'followed' => $id]))
);
DB::flush();
} catch (UniqueConstraintViolationException $e) { } catch (UniqueConstraintViolationException $e) {
throw new NicknameTakenException; throw new NicknameTakenException;
} }
@ -123,11 +125,6 @@ class Security extends Controller
$user->setIsEmailVerified(true); $user->setIsEmailVerified(true);
} }
// Self follow
$follow = Follow::create(['follower' => $id, 'followed' => $id]);
DB::persist($follow);
DB::flush();
return $guard_handler->authenticateUserAndHandleSuccess( return $guard_handler->authenticateUserAndHandleSuccess(
$user, $user,
$request, $request,

View File

@ -173,6 +173,24 @@ abstract class DB
return $repo->count($table, $criteria); return $repo->count($table, $criteria);
} }
/**
* Insert all given objects with the generated ID of the first one
*/
public static function persistWithSameId(object $owner, object | array $others, ?callable $extra = null)
{
$conn = self::getConnection();
$metadata = self::getClassMetadata(get_class($owner));
$seqName = $metadata->getSequenceName($conn->getDatabasePlatform());
self::persist($owner);
$id = $conn->lastInsertId($seqName);
F\map(is_array($others) ? $others : [$others], function ($o) use ($id) { $o->setId($id); self::persist($o); });
if (!is_null($extra)) {
$extra($id);
}
self::flush();
return $id;
}
/** /**
* Intercept static function calls to allow refering to entities * Intercept static function calls to allow refering to entities
* without writing the namespace (which is deduced from the call * without writing the namespace (which is deduced from the call