Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ These **must** be run before committing code, from the root of the monorepo:

## Changelog Guidelines

**Every change must include a changelog update** — do this automatically, without waiting to be asked:
Before committing, **every change must include a changelog update**. Once a change is completed, ask the user if he wants to update the changelog. If yes, follow these steps:

1. Update the relevant `packages/*/CHANGELOG.md` file(s) for affected packages
2. Add entries under the `## [Unreleased]` section
Expand Down
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,12 @@
"package.json": "package-lock.json, pnpm*, .yarnrc*, yarn*, .eslint*, eslint*, .oxlint*, oxlint*, .prettier*, prettier*, .editorconfig",
"composer.json": "composer.lock, phpunit.xml*, phpstan.neon*, phpstan-baseline.neon*, .php-cs*, .php_cs*, .styleci.yml*, .phpdoc.*"
},
"cSpell.words": ["axios", "pinia", "Sprunjer", "tdate", "TOTP"]
"cSpell.words": [
"axios",
"Morphable",
"pinia",
"Sprunjer",
"tdate",
"TOTP"
]
}
2,127 changes: 860 additions & 1,267 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions packages/sprinkle-account/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Changed
- Added activity logging for profile changes.

### New Feature
- Added MorphMapProvider for Eloquent polymorphic type aliases.

Expand Down
20 changes: 20 additions & 0 deletions packages/sprinkle-account/app/src/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\DropVerificationsTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\UpdateUsersTable as V600UpdateUsersTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v600\UserVerificationTable;
use UserFrosting\Sprinkle\Account\Database\Migrations\v610\ActivitiesV2Table;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultGroups;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultPermissions;
use UserFrosting\Sprinkle\Account\Database\Seeds\DefaultRoles;
Expand All @@ -48,15 +49,19 @@
use UserFrosting\Sprinkle\Account\Listener\UpgradePassword;
use UserFrosting\Sprinkle\Account\Listener\UserLogoutActivity;
use UserFrosting\Sprinkle\Account\Listener\UserSignInActivity;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Account\Routes\AuthRoutes;
use UserFrosting\Sprinkle\Account\ServicesProvider\AccessConditionsService;
use UserFrosting\Sprinkle\Account\ServicesProvider\ActivityService;
use UserFrosting\Sprinkle\Account\ServicesProvider\AuthorizationService;
use UserFrosting\Sprinkle\Account\ServicesProvider\AuthService;
use UserFrosting\Sprinkle\Account\ServicesProvider\I18nService;
use UserFrosting\Sprinkle\Account\ServicesProvider\LoggersService;
use UserFrosting\Sprinkle\Account\ServicesProvider\MFAServices;
use UserFrosting\Sprinkle\Account\ServicesProvider\ModelsService;
use UserFrosting\Sprinkle\Account\ServicesProvider\MorphMapProvider;
use UserFrosting\Sprinkle\Account\Sprinkle\Recipe\ActivityRecipe;
use UserFrosting\Sprinkle\Account\Twig\AccountExtension;
use UserFrosting\Sprinkle\BakeryRecipe;
use UserFrosting\Sprinkle\Core\Bakery\Event\BakeCommandEvent;
Expand All @@ -68,6 +73,7 @@

class Account implements
SprinkleRecipe,
ActivityRecipe,
MigrationRecipe,
SeedRecipe,
EventListenerRecipe,
Expand Down Expand Up @@ -113,6 +119,17 @@ public function getSprinkles(): array
];
}

/**
* {@inheritDoc}
*/
public function getActivityTypes(): array
{
return [
UserActivityTypes::class,
AccountActivityTypes::class,
];
}

/**
* {@inheritDoc}
*/
Expand All @@ -130,6 +147,7 @@ public function getServices(): array
{
return [
AccessConditionsService::class,
ActivityService::class,
AuthorizationService::class,
AuthService::class,
ModelsService::class,
Expand Down Expand Up @@ -171,6 +189,8 @@ public function getMigrations(): array
V600UpdateUsersTable::class,
DropPasswordResetsTable::class,
DropVerificationsTable::class,
// v610
ActivitiesV2Table::class,
];
}

Expand Down
15 changes: 8 additions & 7 deletions packages/sprinkle-account/app/src/Bakery/CreateUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
use UserFrosting\Sprinkle\Account\Database\Models\User;
use UserFrosting\Sprinkle\Account\Event\UserCreatedEvent;
use UserFrosting\Sprinkle\Account\Exceptions\AccountException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Validators\UserValidation;
use UserFrosting\Sprinkle\Core\Bakery\Helper\DatabaseTest;
use UserFrosting\Sprinkle\Core\Database\Migrator\MigrationRepositoryInterface;
Expand Down Expand Up @@ -70,7 +70,7 @@ class CreateUser extends Command
protected EventDispatcherInterface $eventDispatcher;

#[Inject]
protected UserActivityLoggerInterface $logger;
protected ActivityRecorderInterface $logger;

#[Inject]
protected Capsule $capsule;
Expand Down Expand Up @@ -190,10 +190,11 @@ protected function execute(InputInterface $input, OutputInterface $output)
$user = $this->eventDispatcher->dispatch($event)->user;

// Create activity record
$this->logger->info("User {$user->user_name} account was created.", [
'type' => UserActivityTypes::REGISTER,
'user_id' => $user->id,
]);
$this->logger->record(
user: $user, // TODO : Should be null, as the command line user is not a user.
type: AccountActivityTypes::CREATE,
subject: $user
);

return $user;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\FailedVerificationException;
use UserFrosting\Sprinkle\Account\Exceptions\VerificationDisabledException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Throttle\Throttler;
Expand Down Expand Up @@ -73,7 +73,7 @@ class EmailVerificationValidationAction
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param UserInterface $userModel
* @param UserActivityLoggerInterface $logger
* @param ActivityRecorderInterface $logger
* @param Connection $db
* @param Throttler $throttler
*/
Expand All @@ -84,7 +84,7 @@ public function __construct(
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator,
protected UserInterface $userModel,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected Connection $db,
protected Throttler $throttler,
protected Config $config,
Expand Down Expand Up @@ -163,10 +163,11 @@ protected function handle(Request $request): void
$user->save();

// Create activity record
$this->logger->info("User {$user->user_name} verified it's account.", [
'type' => UserActivityTypes::VERIFIED,
'user_id' => $user->id,
]);
$this->logger->record(
user: $user,
type: UserActivityTypes::VERIFIED,
subject: $user
);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use UserFrosting\Sprinkle\Account\Authenticate\Interfaces\EmailVerificationProvider;
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\PasswordResetInvalidException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Account\Log\UserActivityTypes;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;
Expand Down Expand Up @@ -56,15 +56,15 @@ class ForgetPasswordSetPasswordAction
/**
* Inject dependencies.
*
* @param Translator $translator
* @param Config $config
* @param RouteParserInterface $routeParser
* @param EmailVerificationProvider $emailVerification
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param Connection $db
* @param UserInterface $userModel
* @param UserActivityLoggerInterface $logger
* @param Translator $translator
* @param Config $config
* @param RouteParserInterface $routeParser
* @param EmailVerificationProvider $emailVerification
* @param RequestDataTransformer $transformer
* @param ServerSideValidator $validator
* @param Connection $db
* @param UserInterface $userModel
* @param ActivityRecorderInterface $logger
*/
public function __construct(
protected Translator $translator,
Expand All @@ -75,7 +75,7 @@ public function __construct(
protected ServerSideValidator $validator,
protected Connection $db,
protected UserInterface $userModel,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
) {
}

Expand Down Expand Up @@ -134,10 +134,11 @@ protected function handle(Request $request): void
$user->save();

// Create activity record
$this->logger->info("User {$user->user_name} reset it's password.", [
'type' => UserActivityTypes::PASSWORD_RESET,
'user_id' => $user->id,
]);
$this->logger->record(
user: $user,
type: UserActivityTypes::PASSWORD_RESET,
subject: $user
);
});
}

Expand Down
30 changes: 19 additions & 11 deletions packages/sprinkle-account/app/src/Controller/ProfileEditAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace UserFrosting\Sprinkle\Account\Controller;

use Illuminate\Database\Connection;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use UserFrosting\Fortress\RequestSchema;
Expand All @@ -23,7 +24,8 @@
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
use UserFrosting\Sprinkle\Account\Exceptions\ForbiddenException;
use UserFrosting\Sprinkle\Account\Exceptions\LocaleNotFoundException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\I18n\SiteLocale;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;
Expand Down Expand Up @@ -53,9 +55,10 @@ public function __construct(
protected Translator $translator,
protected Authenticator $authenticator,
protected SiteLocale $locale,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator
protected ServerSideValidator $validator,
protected Connection $db
) {
}

Expand Down Expand Up @@ -124,14 +127,19 @@ protected function handle(Request $request): void
// Looks good, let's update with new values!
// Note that only fields listed in `profile-settings.yaml` will be
// permitted in $data, so this prevents the user from updating all columns in the DB
$currentUser->fill($data);
$currentUser->save();

// Create activity record
$this->logger->info("User {$currentUser->user_name} updated their profile settings.", [
'type' => 'update_profile_settings',
'user_id' => $currentUser->id,
]);
$this->db->transaction(function () use ($currentUser, $data): void {
$currentUser->fill($data);

// Record while the subject still contains its dirty attributes.
$this->logger->record(
user: $currentUser,
type: AccountActivityTypes::UPDATE_PROFILE_SETTINGS,
subject: $currentUser,
withProperties: true,
);

$currentUser->save();
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace UserFrosting\Sprinkle\Account\Controller;

use Illuminate\Database\Connection;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use UserFrosting\Config\Config;
Expand All @@ -25,7 +26,8 @@
use UserFrosting\Sprinkle\Account\Exceptions\EmailNotUniqueException;
use UserFrosting\Sprinkle\Account\Exceptions\ForbiddenException;
use UserFrosting\Sprinkle\Account\Exceptions\PasswordInvalidException;
use UserFrosting\Sprinkle\Account\Log\UserActivityLoggerInterface;
use UserFrosting\Sprinkle\Account\Log\AccountActivityTypes;
use UserFrosting\Sprinkle\Account\Log\ActivityRecorderInterface;
use UserFrosting\Sprinkle\Core\Exceptions\ValidationException;
use UserFrosting\Sprinkle\Core\Util\ApiResponse;

Expand Down Expand Up @@ -55,10 +57,11 @@ public function __construct(
protected Translator $translator,
protected Authenticator $authenticator,
protected Config $config,
protected UserActivityLoggerInterface $logger,
protected ActivityRecorderInterface $logger,
protected UserInterface $userModel,
protected RequestDataTransformer $transformer,
protected ServerSideValidator $validator
protected ServerSideValidator $validator,
protected Connection $db
) {
}

Expand Down Expand Up @@ -123,17 +126,28 @@ protected function handle(Request $request): void
throw new EmailNotUniqueException();
}

// Keep the old email for activity log
$oldEmail = $currentUser->email;

// Looks good, let's update with new values!
// Note that only fields listed in `account-email.yaml` will be
// permitted in $data, so this prevents the user from updating all columns in the DB
$currentUser->fill($data);
$currentUser->save();

// Create activity record
$this->logger->info("User {$currentUser->user_name} updated their account settings.", [
'type' => 'update_account_settings',
'user_id' => $currentUser->id,
]);
$this->db->transaction(function () use ($currentUser, $data, $oldEmail): void {
$currentUser->fill($data);

// Record while the subject still contains its dirty attributes.
$this->logger->record(
user: $currentUser,
type: AccountActivityTypes::UPDATE_EMAIL,
subject: $currentUser,
metadata: [
'old' => $oldEmail,
'new' => $data['email'],
]
);

$currentUser->save();
});
}

/**
Expand Down
Loading
Loading