diff --git a/Notesnook.API/Authorization/SyncRequirement.cs b/Notesnook.API/Authorization/SyncRequirement.cs
index bf60532..eb249f2 100644
--- a/Notesnook.API/Authorization/SyncRequirement.cs
+++ b/Notesnook.API/Authorization/SyncRequirement.cs
@@ -1,102 +1,102 @@
-/*
-This file is part of the Notesnook Sync Server project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the Affero GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-Affero GNU General Public License for more details.
-
-You should have received a copy of the Affero GNU General Public License
-along with this program. If not, see .
-*/
-
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Claims;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Authorization;
-using Microsoft.AspNetCore.Authorization.Policy;
-using Microsoft.AspNetCore.Http;
-
-namespace Notesnook.API.Authorization
-{
- public class SyncRequirement : AuthorizationHandler, IAuthorizationRequirement
- {
- private readonly Dictionary pathErrorPhraseMap = new()
- {
- ["/sync/attachments"] = "use attachments",
- ["/sync"] = "sync your notes",
- ["/hubs/sync"] = "sync your notes",
- ["/hubs/sync/v2"] = "sync your notes",
- ["/monographs"] = "publish monographs"
- };
-
- protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SyncRequirement requirement)
- {
- PathString path = context.Resource is DefaultHttpContext httpContext ? httpContext.Request.Path : null;
- var result = this.IsAuthorized(context.User, path);
- if (result.Succeeded) context.Succeed(requirement);
- else if (result.AuthorizationFailure.FailureReasons.Any())
- context.Fail(result.AuthorizationFailure.FailureReasons.First());
- else context.Fail();
-
- return Task.CompletedTask;
- }
-
- public PolicyAuthorizationResult IsAuthorized(ClaimsPrincipal User, PathString requestPath)
- {
- var id = User.FindFirstValue("sub");
-
- if (string.IsNullOrEmpty(id))
- {
- var reason = new[]
- {
- new AuthorizationFailureReason(this, "Invalid token.")
- };
- return PolicyAuthorizationResult.Forbid(AuthorizationFailure.Failed(reason));
- }
-
- var hasSyncScope = User.HasClaim("scope", "notesnook.sync");
- var isInAudience = User.HasClaim("aud", "notesnook");
- var hasRole = User.HasClaim("role", "notesnook");
-
- var isEmailVerified = User.HasClaim("verified", "true");
-
- if (!isEmailVerified)
- {
- var phrase = "continue";
-
- foreach (var item in pathErrorPhraseMap)
- {
- if (requestPath != null && requestPath.StartsWithSegments(item.Key))
- phrase = item.Value;
- }
-
- var error = $"Please confirm your email to {phrase}.";
- var reason = new[]
- {
- new AuthorizationFailureReason(this, error)
- };
- return PolicyAuthorizationResult.Forbid(AuthorizationFailure.Failed(reason));
- // context.Fail(new AuthorizationFailureReason(this, error));
- }
-
- if (hasSyncScope && isInAudience && hasRole && isEmailVerified)
- return PolicyAuthorizationResult.Success(); //(requirement);
- return PolicyAuthorizationResult.Forbid();
- }
-
- public override Task HandleAsync(AuthorizationHandlerContext context)
- {
- return this.HandleRequirementAsync(context, this);
- }
-
- }
+/*
+This file is part of the Notesnook Sync Server project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the Affero GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Affero GNU General Public License for more details.
+
+You should have received a copy of the Affero GNU General Public License
+along with this program. If not, see .
+*/
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Authorization.Policy;
+using Microsoft.AspNetCore.Http;
+
+namespace Notesnook.API.Authorization
+{
+ public class SyncRequirement : AuthorizationHandler, IAuthorizationRequirement
+ {
+ private readonly Dictionary pathErrorPhraseMap = new()
+ {
+ ["/sync/attachments"] = "use attachments",
+ ["/sync"] = "sync your notes",
+ ["/hubs/sync"] = "sync your notes",
+ ["/hubs/sync/v2"] = "sync your notes",
+ ["/monographs"] = "publish monographs"
+ };
+
+ protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SyncRequirement requirement)
+ {
+ PathString path = context.Resource is DefaultHttpContext httpContext ? httpContext.Request.Path : null;
+ var result = this.IsAuthorized(context.User, path);
+ if (result.Succeeded) context.Succeed(requirement);
+ else if (result.AuthorizationFailure.FailureReasons.Any())
+ context.Fail(result.AuthorizationFailure.FailureReasons.First());
+ else context.Fail();
+
+ return Task.CompletedTask;
+ }
+
+ public PolicyAuthorizationResult IsAuthorized(ClaimsPrincipal? User, PathString requestPath)
+ {
+ var id = User?.FindFirstValue("sub");
+
+ if (string.IsNullOrEmpty(id))
+ {
+ var reason = new[]
+ {
+ new AuthorizationFailureReason(this, "Invalid token.")
+ };
+ return PolicyAuthorizationResult.Forbid(AuthorizationFailure.Failed(reason));
+ }
+
+ var hasSyncScope = User.HasClaim("scope", "notesnook.sync");
+ var isInAudience = User.HasClaim("aud", "notesnook");
+ var hasRole = User.HasClaim("role", "notesnook");
+
+ var isEmailVerified = User.HasClaim("verified", "true");
+
+ if (!isEmailVerified)
+ {
+ var phrase = "continue";
+
+ foreach (var item in pathErrorPhraseMap)
+ {
+ if (requestPath != null && requestPath.StartsWithSegments(item.Key))
+ phrase = item.Value;
+ }
+
+ var error = $"Please confirm your email to {phrase}.";
+ var reason = new[]
+ {
+ new AuthorizationFailureReason(this, error)
+ };
+ return PolicyAuthorizationResult.Forbid(AuthorizationFailure.Failed(reason));
+ // context.Fail(new AuthorizationFailureReason(this, error));
+ }
+
+ if (hasSyncScope && isInAudience && hasRole && isEmailVerified)
+ return PolicyAuthorizationResult.Success(); //(requirement);
+ return PolicyAuthorizationResult.Forbid();
+ }
+
+ public override Task HandleAsync(AuthorizationHandlerContext context)
+ {
+ return this.HandleRequirementAsync(context, this);
+ }
+
+ }
}
\ No newline at end of file
diff --git a/Notesnook.API/Hubs/SyncV2Hub.cs b/Notesnook.API/Hubs/SyncV2Hub.cs
index 2609d33..27390cd 100644
--- a/Notesnook.API/Hubs/SyncV2Hub.cs
+++ b/Notesnook.API/Hubs/SyncV2Hub.cs
@@ -74,10 +74,10 @@ namespace Notesnook.API.Hubs
var result = new SyncRequirement().IsAuthorized(Context.User, new PathString("/hubs/sync/v2"));
if (!result.Succeeded)
{
- var reason = result.AuthorizationFailure.FailureReasons.FirstOrDefault();
+ var reason = result.AuthorizationFailure?.FailureReasons.FirstOrDefault();
throw new HubException(reason?.Message ?? "Unauthorized");
}
- var id = Context.User.FindFirstValue("sub");
+ var id = Context.User?.FindFirstValue("sub") ?? throw new HubException("User not found.");
await Groups.AddToGroupAsync(Context.ConnectionId, id);
await base.OnConnectedAsync();
}
@@ -103,13 +103,11 @@ namespace Notesnook.API.Hubs
public async Task PushItems(string deviceId, SyncTransferItemV2 pushItem)
{
- var userId = Context.User.FindFirstValue("sub");
- if (string.IsNullOrEmpty(userId)) throw new HubException("Please login to sync.");
+ var userId = Context.User?.FindFirstValue("sub") ?? throw new HubException("Please login to sync.");
SyncEventCounterSource.Log.PushV2();
- var stopwatch = new Stopwatch();
- stopwatch.Start();
+ var stopwatch = Stopwatch.StartNew();
try
{
@@ -123,14 +121,13 @@ namespace Notesnook.API.Hubs
}
finally
{
- stopwatch.Stop();
SyncEventCounterSource.Log.RecordPushDuration(stopwatch.ElapsedMilliseconds);
}
}
public async Task PushCompleted()
{
- var userId = Context.User.FindFirstValue("sub");
+ var userId = Context.User?.FindFirstValue("sub") ?? throw new HubException("User not found.");
await Clients.OthersInGroup(userId).PushCompleted();
return true;
}
@@ -197,8 +194,7 @@ namespace Notesnook.API.Hubs
private async Task HandleRequestFetch(string deviceId, bool includeMonographs)
{
- var userId = Context.User.FindFirstValue("sub");
- if (string.IsNullOrEmpty(userId)) throw new HubException("Please login to sync.");
+ var userId = Context.User?.FindFirstValue("sub") ?? throw new HubException("Please login to sync.");
SyncEventCounterSource.Log.FetchV2();
@@ -214,8 +210,7 @@ namespace Notesnook.API.Hubs
!isResetSync)
return new SyncV2Metadata { Synced = true };
- var stopwatch = new Stopwatch();
- stopwatch.Start();
+ var stopwatch = Stopwatch.StartNew();
try
{
string[] ids = deviceService.FetchUnsyncedIds();
@@ -285,7 +280,6 @@ namespace Notesnook.API.Hubs
}
finally
{
- stopwatch.Stop();
SyncEventCounterSource.Log.RecordFetchDuration(stopwatch.ElapsedMilliseconds);
}
}
diff --git a/Notesnook.API/Interfaces/IS3Service.cs b/Notesnook.API/Interfaces/IS3Service.cs
index 509d9c3..6c83d38 100644
--- a/Notesnook.API/Interfaces/IS3Service.cs
+++ b/Notesnook.API/Interfaces/IS3Service.cs
@@ -1,40 +1,40 @@
-/*
-This file is part of the Notesnook Sync Server project (https://notesnook.com/)
-
-Copyright (C) 2023 Streetwriters (Private) Limited
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the Affero GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-Affero GNU General Public License for more details.
-
-You should have received a copy of the Affero GNU General Public License
-along with this program. If not, see .
-*/
-
-using System.Threading;
-using System.Threading.Tasks;
-using Amazon.S3.Model;
-using Notesnook.API.Models;
-using Notesnook.API.Models.Responses;
-using Streetwriters.Common.Interfaces;
-
-namespace Notesnook.API.Interfaces
-{
- public interface IS3Service
- {
- Task DeleteObjectAsync(string userId, string name);
- Task DeleteDirectoryAsync(string userId);
- Task GetObjectSizeAsync(string userId, string name);
- string GetUploadObjectUrl(string userId, string name);
- string GetDownloadObjectUrl(string userId, string name);
- Task StartMultipartUploadAsync(string userId, string name, int parts, string uploadId = null);
- Task AbortMultipartUploadAsync(string userId, string name, string uploadId);
- Task CompleteMultipartUploadAsync(string userId, CompleteMultipartUploadRequest uploadRequest);
- }
+/*
+This file is part of the Notesnook Sync Server project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the Affero GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+Affero GNU General Public License for more details.
+
+You should have received a copy of the Affero GNU General Public License
+along with this program. If not, see .
+*/
+
+using System.Threading;
+using System.Threading.Tasks;
+using Amazon.S3.Model;
+using Notesnook.API.Models;
+using Notesnook.API.Models.Responses;
+using Streetwriters.Common.Interfaces;
+
+namespace Notesnook.API.Interfaces
+{
+ public interface IS3Service
+ {
+ Task DeleteObjectAsync(string userId, string name);
+ Task DeleteDirectoryAsync(string userId);
+ Task GetObjectSizeAsync(string userId, string name);
+ string? GetUploadObjectUrl(string userId, string name);
+ string GetDownloadObjectUrl(string userId, string name);
+ Task StartMultipartUploadAsync(string userId, string name, int parts, string uploadId = null);
+ Task AbortMultipartUploadAsync(string userId, string name, string uploadId);
+ Task CompleteMultipartUploadAsync(string userId, CompleteMultipartUploadRequest uploadRequest);
+ }
}
\ No newline at end of file
diff --git a/Notesnook.API/Services/S3Service.cs b/Notesnook.API/Services/S3Service.cs
index e6afa3f..41a0e4f 100644
--- a/Notesnook.API/Services/S3Service.cs
+++ b/Notesnook.API/Services/S3Service.cs
@@ -155,11 +155,9 @@ namespace Notesnook.API.Services
}
- public string GetUploadObjectUrl(string userId, string name)
+ public string? GetUploadObjectUrl(string userId, string name)
{
- var url = this.GetPresignedURL(userId, name, HttpVerb.PUT);
- if (url == null) return null;
- return url;
+ return this.GetPresignedURL(userId, name, HttpVerb.PUT);
}
public string GetDownloadObjectUrl(string userId, string name)
@@ -215,7 +213,7 @@ namespace Notesnook.API.Services
if (!IsSuccessStatusCode(((int)response.HttpStatusCode))) throw new Exception("Failed to complete multipart upload.");
}
- private string GetPresignedURL(string userId, string name, HttpVerb httpVerb, S3ClientMode mode = S3ClientMode.EXTERNAL)
+ private string? GetPresignedURL(string userId, string name, HttpVerb httpVerb, S3ClientMode mode = S3ClientMode.EXTERNAL)
{
var objectName = GetFullObjectName(userId, name);
if (userId == null || objectName == null) return null;