-
Notifications
You must be signed in to change notification settings - Fork 644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Organizations: transform account on confirmation #5228
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good in general, I'm just curious why a SQL script was necessary instead of using entity framework directly. Whatever the reason, it should be in a comment in the code.
|
||
[HttpGet] | ||
[Authorize] | ||
public virtual async Task<ActionResult> ConfirmTransform(string accountName, string token) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accountName
should probably be accountToTransform
for clarity
@@ -0,0 +1,47 @@ | |||
-- Copyright (c) .NET Foundation. All rights reserved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a script and not done through EntityFramework?
I can't see anything here that would make it necessary to use a script. We've done transactions through EF before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EF has a limitation with inheritance in that it doesn't support changing an object's type. It must be done with SQL. I can add a comment in UserService.TransformToOrganizationAccount.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding transactions, note that by using a sproc here I also don't need a custom transaction and suspended execution strategy in the Gallery code.
The disadvantage of suspending execution strategy in Gallery code is that I believe it disables SqlAzure strategy defaults such as retry logic.
|
||
BEGIN TRANSACTION | ||
BEGIN TRY | ||
-- Ensure Organizations do not have credentials or memberships |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't "ensuring", this is wiping any of these existing entities if they exist.
IMO we should fail the migration if the user has any memberships--that would suggest that the user was already an organization, which is not something we should just ignore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable... I'll add a SELECT outside the transaction and fail fast if the account has any memberships.
Gallery-side, we should also check for memberships before allowing migration. Account would need to be removed from organization(s) to be eligible for migration.
|
||
private bool CanTransformIntoOrganization(User user) | ||
{ | ||
if (!user.Confirmed || user.IsAdministrator()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would assume if that user was already an organization, we would also want to reject the transformation. Any reason this condition isn't also filtered here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could add additional validation here to guard against it.
src/NuGetGallery/Strings.resx
Outdated
@@ -688,4 +688,25 @@ For more information, please contact '{2}'.</value> | |||
<data name="PackageIsLocked" xml:space="preserve"> | |||
<value>Package '{0}' has been locked. This means you cannot publish a new version or change the listing status of a published package version. Please contact support@nuget.org.</value> | |||
</data> | |||
<data name="TransformAccount_AdminDoesNotHaveTenantId" xml:space="preserve"> | |||
<value>Failed to transform the account because you do not have an Azure AD sign-in with tenant ID.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about: You account must have an Azure AD sign-in with a tenant ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be shown on confirmation (admin sign-in), so it's not 'Your account'.
/cc @anangaur
Anand, let me know if you want to provide the exact error messages for these migration failure scenarios. Spec didn't go into details on this.
src/NuGetGallery/Strings.resx
Outdated
<value>Your account must be confirmed before you can create an organization.</value> | ||
</data> | ||
<data name="TransformAccount_DatabaseError" xml:space="preserve"> | ||
<value>Failed to transform the account due to a database error.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Database error" is a little bit cryptic to the average user.
How about: There was an unexpected error in transforming the account.
You can also throw in Contact support
or something like that if this is something they should contact us about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would happen if the migration request didn't exist, a DB constraint prevented the update, etc. Probably not very likely. I'm ok with 'unexpected error'.
<h1> | ||
Transform Account: Failed | ||
</h1> | ||
@if (TempData.ContainsKey("TransformError")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there isn't a TransformError
, we should probably should a default message of some sort.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On migration success, this should redirect to the (future) 'Manage Organization' page. For now, I redirect to the admin account page.
I only added this view because I needed a way of displaying an error to the user.
{ | ||
// Arrange | ||
var controller = GetController<UsersController>(); | ||
var currentUser = new User() { UnconfirmedEmailAddress = "unconfirmed@example.com" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: might be easier to use users from Fakes for this
E.g.
var fakes = Get<Fakes>();
var currentUser = fakes.User;
The advantages are clearer when you need an Admin user below:
var fakes = Get<Fakes>();
var adminUser = fakes.AdminUser;
src/NuGetGallery/Web.config
Outdated
@@ -41,6 +41,7 @@ | |||
|
|||
<add key="Gallery.AsynchronousPackageValidationEnabled" value="false" /> | |||
<add key="Gallery.BlockingAsynchronousPackageValidationEnabled" value="false" /> | |||
<add key="Gallery.OrganizationsEnabledForDomains" value="microsoft.com" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
microsoft.com should be set in our private configs, not the default ones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Forgot to remove it - will do
} | ||
catch (TransformAccountException e) | ||
{ | ||
TempData["TransformError"] = e.AsUserSafeException().GetUserSafeMessage(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TransformAccountException doesn't inherit from UserSafeException, so we will always show a generic message here. Is this the intention?
|
||
if (string.IsNullOrWhiteSpace(token)) | ||
{ | ||
throw new ArgumentNullException(nameof(token)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ArgumentException since the value isn't necessarily null
src/NuGetGallery/Strings.resx
Outdated
@@ -688,4 +688,22 @@ For more information, please contact '{2}'.</value> | |||
<data name="PackageIsLocked" xml:space="preserve"> | |||
<value>Package '{0}' has been locked. This means you cannot publish a new version or change the listing status of a published package version. Please contact support@nuget.org.</value> | |||
</data> | |||
<data name="TransformAccount_AdminDoesNotHaveTenantId" xml:space="preserve"> | |||
<value>Failed to transform the account because you do not have an Azure AD sign-in with tenant ID. Contact support@nuget.org for more details.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the average user knows what a tenant id is. Please go over the messages with Anand :)
Per discussion in stand-up, I'm going to see if I can create the migration SQL script as an embedded resource and just invoke it inline from the Gallery. Having DB tests sounds nice, but this sounds like less work. |
catch (Exception ex) when (ex is SqlException || ex is DataException) | ||
{ | ||
// EF exception when saving account transformation to the database. | ||
throw new TransformAccountException(Strings.TransformAccount_DatabaseError); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exceptions you throw here are swallowed in UsersController, so we have no log of them. Please add a trace (somewhere)
@chenriksson , we will need DB tests anyhow, since if the script breaks, there's no way to know about it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🕐
9ee1ec5
to
12df6a3
Compare
@skofman1 You're right - we still need the DB tests. I've reverted the migration with sproc, and instead execute SQL via embedded resource. |
sqlCommand = await reader.ReadToEndAsync(); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(sqlCommand)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't execute an empty command be indicative of a bug? I think we should throw here -- or let ExecuteSqlCommandAsync
do its thing.
|
||
public async Task<bool> TransformUserToOrganization(User accountToTransform, User adminUser, string token) | ||
{ | ||
// todo: check for tenantId and add organization policy to enforce this (future work, with manage organization) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Track TODO unless it's gonna happen very soon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's my next work item. I've been tracking all migration with #5112, but can split this into smaller tasks.
Proposal: Failed to transform to an organization'Microsoft' is already an organization on NuGet.org. |
} | ||
catch (Exception ex) when (ex is SqlException || ex is DataException) | ||
{ | ||
// todo: log exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
log?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed error handling, which seems more consistent with how we handle SQL exceptions in other places.
@chenriksson , looks good! Please add a log and then |
Screenshot of error, if transform fails. Will confirm messages with @anangaur before merging.