moved from ddf515b8

This commit is contained in:
pavelbannov 2022-06-09 18:36:57 +03:00
parent 9edcce8858
commit a9b826f8f5
21 changed files with 865 additions and 669 deletions

View File

@ -31,6 +31,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.19.0" />
</ItemGroup>
<ItemGroup>

View File

@ -26,21 +26,21 @@
global using System.Data;
global using System.Diagnostics;
global using System.Globalization;
global using System.Net;
global using System.Net.Http.Headers;
global using System.Reflection;
global using System.Runtime.Serialization;
global using System.Security.Claims;
global using System.Security.Cryptography;
global using System.Security.Cryptography.Pkcs;
global using System.Security.Cryptography.X509Certificates;
global using System.Text;
global using System.Text.Json.Serialization;
global using System.Web;
global using System.Xml.Linq;
global using System.Xml.XPath;
global using ASC.Common;
global using ASC.Common.Caching;
global using ASC.Common.Log;
global using ASC.Common.Utils;
global using ASC.Core;
global using ASC.Core.Common.Configuration;
@ -54,16 +54,12 @@ global using ASC.FederatedLogin.Helpers;
global using ASC.FederatedLogin.LoginProviders;
global using ASC.FederatedLogin.Profile;
global using ASC.Security.Cryptography;
global using ASC.Web.Core.Files;
global using Autofac;
global using DotNetOpenAuth.Messaging;
global using DotNetOpenAuth.OpenId;
global using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
global using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
global using DotNetOpenAuth.OpenId.RelyingParty;
global using JWT.Algorithms;
global using JWT.Builder;
global using Microsoft.AspNetCore.Builder;
global using Microsoft.AspNetCore.Hosting;
global using Microsoft.AspNetCore.Http;
@ -72,7 +68,10 @@ global using Microsoft.AspNetCore.WebUtilities;
global using Microsoft.EntityFrameworkCore;
global using Microsoft.Extensions.Caching.Memory;
global using Microsoft.Extensions.Configuration;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Options;
global using Newtonsoft.Json;
global using Newtonsoft.Json.Linq;
global using Newtonsoft.Json.Linq;
global using SecurityContext = ASC.Core.SecurityContext;

View File

@ -0,0 +1,197 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
/*
*
* (c) Copyright Ascensio System Limited 2010-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
namespace ASC.FederatedLogin.LoginProviders;
[Scope]
public class AppleIdLoginProvider : BaseLoginProvider<AppleIdLoginProvider>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly RequestHelper _requestHelper;
public override string AccessTokenUrl { get { return "https://appleid.apple.com/auth/token"; } }
public override string RedirectUri { get { return this["appleIdRedirectUrl"]; } }
public override string ClientID { get { return (this["appleIdClientIdMobile"] != null && _httpContextAccessor?.HttpContext != null && _httpContextAccessor.HttpContext.Request.MobileApp()) ? this["appleIdClientIdMobile"] : this["appleIdClientId"]; } }
public override string ClientSecret => GenerateSecret();
public override string CodeUrl { get { return "https://appleid.apple.com/auth/authorize"; } }
public override string Scopes { get { return ""; } }
public string TeamId { get { return this["appleIdTeamId"]; } }
public string KeyId { get { return this["appleIdKeyId"]; } }
public string PrivateKey { get { return this["appleIdPrivateKey"]; } }
public AppleIdLoginProvider() { }
public AppleIdLoginProvider(
OAuth20TokenHelper oAuth20TokenHelper,
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
ConsumerFactory consumerFactory,
Signature signature,
InstanceCrypto instanceCrypto,
IHttpContextAccessor httpContextAccessor,
RequestHelper requestHelper,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional = null)
: base(oAuth20TokenHelper, tenantManager, coreBaseSettings, coreSettings, configuration, cache, consumerFactory, signature, instanceCrypto, name, order, props, additional)
{
_httpContextAccessor = httpContextAccessor;
_requestHelper = requestHelper;
}
public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs)
{
try
{
var token = Auth(context, Scopes, out var redirect, @params, additionalStateArgs);
var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value<string>("id_token"));
return GetProfileFromClaims(claims);
}
catch (ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
return LoginProfile.FromError(Signature, InstanceCrypto, ex);
}
}
public override LoginProfile GetLoginProfile(string authCode)
{
if (string.IsNullOrEmpty(authCode))
{
throw new Exception("Login failed");
}
var token = _oAuth20TokenHelper.GetAccessToken<AppleIdLoginProvider>(ConsumerFactory, authCode);
var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value<string>("id_token"));
return GetProfileFromClaims(claims);
}
public override LoginProfile GetLoginProfile(OAuth20Token token)
{
if (token == null)
{
throw new Exception("Login failed");
}
var claims = ValidateIdToken(JObject.Parse(token.OriginJson).Value<string>("id_token"));
return GetProfileFromClaims(claims);
}
private LoginProfile GetProfileFromClaims(ClaimsPrincipal claims)
{
return new LoginProfile(Signature, InstanceCrypto)
{
Id = claims.FindFirst(ClaimTypes.NameIdentifier).Value,
EMail = claims.FindFirst(ClaimTypes.Email)?.Value,
Provider = ProviderConstants.AppleId,
};
}
private string GenerateSecret()
{
using (var cngKey = CngKey.Import(Convert.FromBase64String(PrivateKey), CngKeyBlobFormat.Pkcs8PrivateBlob))
{
var handler = new JwtSecurityTokenHandler();
var token = handler.CreateJwtSecurityToken(
issuer: TeamId,
audience: "https://appleid.apple.com",
subject: new ClaimsIdentity(new List<Claim> { new Claim("sub", ClientID) }),
issuedAt: DateTime.UtcNow,
notBefore: DateTime.UtcNow,
expires: DateTime.UtcNow.AddMinutes(5),
signingCredentials: new SigningCredentials(new ECDsaSecurityKey(new ECDsaCng(cngKey)), SecurityAlgorithms.EcdsaSha256)
);
token.Header.Add("kid", KeyId);
return handler.WriteToken(token);
}
}
private ClaimsPrincipal ValidateIdToken(string idToken)
{
var handler = new JwtSecurityTokenHandler();
var claims = handler.ValidateToken(idToken, new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKeys = GetApplePublicKeys(),
ValidateIssuer = true,
ValidIssuer = "https://appleid.apple.com",
ValidateAudience = true,
ValidAudience = ClientID,
ValidateLifetime = true
}, out var _);
return claims;
}
private IEnumerable<SecurityKey> GetApplePublicKeys()
{
var appplePublicKeys = _requestHelper.PerformRequest("https://appleid.apple.com/auth/keys");
var keys = new List<SecurityKey>();
foreach (var webKey in JObject.Parse(appplePublicKeys).Value<JArray>("keys"))
{
var e = Base64UrlEncoder.DecodeBytes(webKey.Value<string>("e"));
var n = Base64UrlEncoder.DecodeBytes(webKey.Value<string>("n"));
var key = new RsaSecurityKey(new RSAParameters { Exponent = e, Modulus = n })
{
KeyId = webKey.Value<string>("kid")
};
keys.Add(key);
}
return keys;
}
}

View File

@ -66,7 +66,7 @@ public abstract class BaseLoginProvider<T> : Consumer, ILoginProvider where T :
internal readonly Signature Signature;
internal readonly InstanceCrypto InstanceCrypto;
private readonly OAuth20TokenHelper _oAuth20TokenHelper;
protected readonly OAuth20TokenHelper _oAuth20TokenHelper;
protected BaseLoginProvider() { }
@ -139,6 +139,16 @@ public abstract class BaseLoginProvider<T> : Consumer, ILoginProvider where T :
return _oAuth20TokenHelper.GetAccessToken<T>(ConsumerFactory, code);
}
public virtual LoginProfile GetLoginProfile(OAuth20Token token)
{
return GetLoginProfile(token.AccessToken);
}
public OAuth20Token GetToken(string codeOAuth)
{
return _oAuth20TokenHelper.GetAccessToken<T>(ConsumerFactory, codeOAuth);
}
}
public static class BaseLoginProviderExtension

View File

@ -29,9 +29,10 @@ namespace ASC.FederatedLogin.LoginProviders;
[Scope]
public class BitlyLoginProvider : Consumer, IValidateKeysProvider
{
private string BitlyClientId => this["bitlyClientId"];
private string BitlyClientSecret => this["bitlyClientSecret"];
private string BitlyUrl => this["bitlyUrl"];
private string BitlyToken => this["bitlyToken"];
private readonly string _bitlyUrl = "https://api-ssl.bitly.com/v4/shorten";
private readonly RequestHelper _requestHelper;
public BitlyLoginProvider() { }
@ -42,9 +43,11 @@ public class BitlyLoginProvider : Consumer, IValidateKeysProvider
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
ConsumerFactory consumerFactory,
RequestHelper requestHelper,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional = null)
: base(tenantManager, coreBaseSettings, coreSettings, configuration, cache, consumerFactory, name, order, props, additional)
{
_requestHelper = requestHelper;
}
public bool ValidateKeys()
@ -63,35 +66,25 @@ public class BitlyLoginProvider : Consumer, IValidateKeysProvider
{
get
{
return !string.IsNullOrEmpty(BitlyClientId) &&
!string.IsNullOrEmpty(BitlyClientSecret) &&
!string.IsNullOrEmpty(BitlyUrl);
return !string.IsNullOrEmpty(BitlyToken);
}
}
public string GetShortenLink(string shareLink)
{
var uri = new Uri(shareLink);
var data = string.Format("{{\"long_url\":\"{0}\"}}", shareLink);
var headers = new Dictionary<string, string>
{
{"Authorization" ,"Bearer " + BitlyToken}
};
var bitly = string.Format(BitlyUrl, BitlyClientId, BitlyClientSecret, Uri.EscapeDataString(uri.ToString()));
XDocument response;
try
{
response = XDocument.Load(bitly);
}
catch (Exception e)
{
throw new InvalidOperationException(e.Message, e);
}
var response = _requestHelper.PerformRequest(_bitlyUrl, "application/json", "POST", data, headers);
var status = response.XPathSelectElement("/response/status_code").Value;
if (status != ((int)HttpStatusCode.OK).ToString(CultureInfo.InvariantCulture))
{
throw new InvalidOperationException(status);
}
var parser = JObject.Parse(response);
if (parser == null) return null;
var data = response.XPathSelectElement("/response/data/url");
var link = parser.Value<string>("link");
return data.Value;
return link;
}
}

View File

@ -24,24 +24,25 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
using SecurityContext = ASC.Core.SecurityContext;
namespace ASC.Web.Studio.Core;
[Scope]
public class EncryptionLoginProvider
{
private readonly ILogger<EncryptionLoginProvider> _logger;
private readonly SecurityContext _securityContext;
private readonly Signature _signature;
private readonly InstanceCrypto _instanceCrypto;
private readonly IOptionsSnapshot<AccountLinker> _snapshot;
public EncryptionLoginProvider(
ILogger<EncryptionLoginProvider> logger,
SecurityContext securityContext,
Signature signature,
InstanceCrypto instanceCrypto,
IOptionsSnapshot<AccountLinker> snapshot)
{
_logger = logger;
_securityContext = securityContext;
_signature = signature;
_instanceCrypto = instanceCrypto;
@ -80,8 +81,15 @@ public class EncryptionLoginProvider
return null;
}
var keys = _instanceCrypto.Decrypt(profile.Name);
return keys;
try
{
return _instanceCrypto.Decrypt(profile.Name);
}
catch (Exception ex)
{
var message = string.Format("Can not decrypt {0} keys for {1}", ProviderConstants.Encryption, userId);
_logger.ErrorWithException(message, ex);
return null;
}
}
}

View File

@ -88,7 +88,9 @@ public class GosUslugiLoginProvider : BaseLoginProvider<GosUslugiLoginProvider>
public override LoginProfile GetLoginProfile(string accessToken)
{
var tokenPayloadString = JsonWebToken.Decode(accessToken, string.Empty, false, true);
var tokenPayloadString = JwtBuilder.Create()
.WithAlgorithm(new HMACSHA256Algorithm())
.Decode(accessToken);
var tokenPayload = JObject.Parse(tokenPayloadString);
if (tokenPayload == null)
{

View File

@ -31,6 +31,10 @@ public interface ILoginProvider : IOAuthProvider
LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs);
LoginProfile GetLoginProfile(string accessToken);
LoginProfile GetLoginProfile(OAuth20Token token);
OAuth20Token GetToken(string codeOAuth);
}
public interface IOAuthProvider

View File

@ -0,0 +1,109 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
/*
*
* (c) Copyright Ascensio System Limited 2010-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace ASC.FederatedLogin.LoginProviders;
public class MicrosoftLoginProvider : BaseLoginProvider<MicrosoftLoginProvider>
{
private const string MicrosoftProfileUrl = "https://graph.microsoft.com/oidc/userinfo";
public override string AccessTokenUrl { get { return "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; } }
public override string RedirectUri { get { return this["microsoftRedirectUrl"]; } }
public override string ClientID { get { return this["microsoftClientId"]; } }
public override string ClientSecret { get { return this["microsoftClientSecret"]; } }
public override string CodeUrl { get { return "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"; } }
public override string Scopes { get { return "openid,email,profile"; } }
private readonly RequestHelper _requestHelper;
public MicrosoftLoginProvider() { }
public MicrosoftLoginProvider(
OAuth20TokenHelper oAuth20TokenHelper,
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
ConsumerFactory consumerFactory,
Signature signature,
InstanceCrypto instanceCrypto,
RequestHelper requestHelper,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional = null)
: base(oAuth20TokenHelper, tenantManager, coreBaseSettings, coreSettings, configuration, cache, consumerFactory, signature, instanceCrypto, name, order, props, additional)
{
_requestHelper = requestHelper;
}
public override LoginProfile GetLoginProfile(string accessToken)
{
if (string.IsNullOrEmpty(accessToken))
{
throw new Exception("Login failed");
}
return RequestProfile(accessToken);
}
private LoginProfile RequestProfile(string accessToken)
{
var openidProfile = _requestHelper.PerformRequest(MicrosoftProfileUrl, headers: new Dictionary<string, string>() { { "Authorization", "Bearer " + accessToken } });
var loginProfile = ProfileFromMicrosoft(openidProfile);
return loginProfile;
}
internal LoginProfile ProfileFromMicrosoft(string openidProfile)
{
var jProfile = JObject.Parse(openidProfile);
if (jProfile == null) throw new Exception("Failed to correctly process the response");
var profile = new LoginProfile(Signature, InstanceCrypto)
{
FirstName = jProfile.Value<string>("given_name"),
LastName = jProfile.Value<string>("family_name"),
EMail = jProfile.Value<string>("email"),
Id = jProfile.Value<string>("sub"),
Provider = ProviderConstants.Microsoft
};
return profile;
}
}

View File

@ -1,178 +0,0 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.FederatedLogin.LoginProviders;
class OpenIdLoginProvider : ILoginProvider
{
public string Scopes => string.Empty;
public string CodeUrl => string.Empty;
public string AccessTokenUrl => string.Empty;
public string RedirectUri => string.Empty;
public string ClientID => string.Empty;
public string ClientSecret => string.Empty;
public bool IsEnabled => _consumerFactory.Get<GoogleLoginProvider>().IsEnabled;
private static readonly OpenIdRelyingParty _openId = new OpenIdRelyingParty();
private readonly Signature _signature;
private readonly InstanceCrypto _instanceCrypto;
private readonly ConsumerFactory _consumerFactory;
public OpenIdLoginProvider(Signature signature, InstanceCrypto instanceCrypto, ConsumerFactory consumerFactory)
{
_signature = signature;
_instanceCrypto = instanceCrypto;
_consumerFactory = consumerFactory;
}
public LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs)
{
var response = _openId.GetResponse();
if (response == null)
{
if (Identifier.TryParse(@params["oid"], out var id))
{
try
{
IAuthenticationRequest request;
var realmUrlString = string.Empty;
@params.TryGetValue("realmUrl", out realmUrlString);
if (!string.IsNullOrEmpty(realmUrlString))
{
request = _openId.CreateRequest(id, new Realm(realmUrlString));
}
else
{
request = _openId.CreateRequest(id);
}
request.AddExtension(new ClaimsRequest
{
Email = DemandLevel.Require,
Nickname = DemandLevel.Require,
Country = DemandLevel.Request,
Gender = DemandLevel.Request,
PostalCode = DemandLevel.Request,
TimeZone = DemandLevel.Request,
FullName = DemandLevel.Request,
});
var fetch = new FetchRequest();
fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
//Duplicating attributes
fetch.Attributes.AddRequired("http://schema.openid.net/contact/email");//Add two more
fetch.Attributes.AddRequired("http://openid.net/schema/contact/email");
fetch.Attributes.AddRequired(WellKnownAttributes.Name.Alias);
fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetch.Attributes.AddRequired(WellKnownAttributes.Media.Images.Default);
fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);
fetch.Attributes.AddRequired(WellKnownAttributes.Name.Middle);
fetch.Attributes.AddRequired(WellKnownAttributes.Person.Gender);
fetch.Attributes.AddRequired(WellKnownAttributes.BirthDate.WholeBirthDate);
request.AddExtension(fetch);
request.RedirectToProvider();
//context.Response.End();//TODO This will throw thread abort
}
catch (ProtocolException ex)
{
return LoginProfile.FromError(_signature, _instanceCrypto, ex);
}
}
else
{
return LoginProfile.FromError(_signature, _instanceCrypto, new Exception("invalid OpenID identifier"));
}
}
else
{
// Stage 3: OpenID Provider sending assertion response
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
var spprofile = response.GetExtension<ClaimsResponse>();
var fetchprofile = response.GetExtension<FetchResponse>();
var realmUrlString = string.Empty;
@params.TryGetValue("realmUrl", out realmUrlString);
var profile = ProfileFromOpenId(spprofile, fetchprofile, response.ClaimedIdentifier.ToString(), realmUrlString);
return profile;
case AuthenticationStatus.Canceled:
return LoginProfile.FromError(_signature, _instanceCrypto, new Exception("Canceled at provider"));
case AuthenticationStatus.Failed:
return LoginProfile.FromError(_signature, _instanceCrypto, response.Exception);
}
}
return null;
}
public LoginProfile GetLoginProfile(string accessToken)
{
throw new NotImplementedException();
}
internal LoginProfile ProfileFromOpenId(ClaimsResponse spprofile, FetchResponse fetchprofile, string claimedId, string realmUrlString)
{
var profile = new LoginProfile(_signature, _instanceCrypto)
{
Link = claimedId,
Id = claimedId,
Provider = ProviderConstants.OpenId,
};
if (spprofile != null)
{
//Fill
profile.BirthDay = spprofile.BirthDateRaw;
profile.DisplayName = spprofile.FullName;
profile.EMail = spprofile.Email;
profile.Name = spprofile.Nickname;
profile.Gender = spprofile.Gender.HasValue ? spprofile.Gender.Value.ToString() : "";
profile.TimeZone = spprofile.TimeZone;
profile.Locale = spprofile.Language;
}
if (fetchprofile != null)
{
profile.Name = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Alias);
profile.LastName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Last);
profile.FirstName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.First);
profile.DisplayName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.FullName);
profile.MiddleName = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Middle);
profile.Salutation = fetchprofile.GetAttributeValue(WellKnownAttributes.Name.Prefix);
profile.Avatar = fetchprofile.GetAttributeValue(WellKnownAttributes.Media.Images.Default);
profile.EMail = fetchprofile.GetAttributeValue(WellKnownAttributes.Contact.Email);
profile.Gender = fetchprofile.GetAttributeValue(WellKnownAttributes.Person.Gender);
profile.BirthDay = fetchprofile.GetAttributeValue(WellKnownAttributes.BirthDate.WholeBirthDate);
}
profile.RealmUrl = realmUrlString;
return profile;
}
}

View File

@ -64,9 +64,7 @@ public class ProviderManager
public ILoginProvider GetLoginProvider(string providerType)
{
return providerType == ProviderConstants.OpenId
? new OpenIdLoginProvider(_signature, _instanceCrypto, _consumerFactory)
: _consumerFactory.GetByKey(providerType) as ILoginProvider;
return _consumerFactory.GetByKey(providerType) as ILoginProvider;
}
public LoginProfile Process(string providerType, HttpContext context, IDictionary<string, string> @params, IDictionary<string, string> additionalStateArgs = null)
@ -74,7 +72,7 @@ public class ProviderManager
return GetLoginProvider(providerType).ProcessAuthoriztion(context, @params, additionalStateArgs);
}
public LoginProfile GetLoginProfile(string providerType, string accessToken)
public LoginProfile GetLoginProfile(string providerType, string accessToken = null, string codeOAuth = null)
{
var consumer = GetLoginProvider(providerType);
if (consumer == null)
@ -84,6 +82,10 @@ public class ProviderManager
try
{
if (accessToken == null && codeOAuth != null)
{
return consumer.GetLoginProfile(consumer.GetToken(codeOAuth));
}
return consumer.GetLoginProfile(accessToken);
}
catch (Exception ex)

View File

@ -24,18 +24,28 @@
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
//using System;
//using System.Collections.Generic;
//using System.Globalization;
//using System.Web;
//using ASC.FederatedLogin.Profile;
//using Microsoft.AspNetCore.Http;
//using Newtonsoft.Json.Linq;
//using Tweetinvi;
//using Tweetinvi.Auth;
//using Tweetinvi.Parameters;
//namespace ASC.FederatedLogin.LoginProviders
//{
// public class TwitterLoginProvider : BaseLoginProvider<TwitterLoginProvider>
// {
// public string TwitterKey { get { return Instance.ClientID; } }
// public string TwitterSecret { get { return Instance.ClientSecret; } }
// public string TwitterDefaultAccessToken { get { return Instance["twitterAccessToken_Default"]; } }
// public string TwitterAccessTokenSecret { get { return Instance["twitterAccessTokenSecret_Default"]; } }
// public static string TwitterKey { get { return Instance.ClientID; } }
// public static string TwitterSecret { get { return Instance.ClientSecret; } }
// public static string TwitterDefaultAccessToken { get { return Instance["twitterAccessToken_Default"]; } }
// public static string TwitterAccessTokenSecret { get { return Instance["twitterAccessTokenSecret_Default"]; } }
// public override string AccessTokenUrl { get { return "https://api.twitter.com/oauth/access_token"; } }
// public override string RedirectUri { get { return this["twitterRedirectUrl"]; } }
@ -43,6 +53,8 @@
// public override string ClientSecret { get { return this["twitterSecret"]; } }
// public override string CodeUrl { get { return "https://api.twitter.com/oauth/request_token"; } }
// private static readonly IAuthenticationRequestStore _myAuthRequestStore = new LocalAuthenticationRequestStore();
// public override bool IsEnabled
// {
// get
@ -57,39 +69,75 @@
// public override LoginProfile ProcessAuthoriztion(HttpContext context, IDictionary<string, string> @params)
// {
// if (!string.IsNullOrEmpty(context.Request.Query["denied"]))
// if (!string.IsNullOrEmpty(context.Request["denied"]))
// {
// return LoginProfile.FromError(new Exception("Canceled at provider"));
// }
// if (string.IsNullOrEmpty(context.Request.Query["oauth_token"]))
// var appClient = new TwitterClient(TwitterKey, TwitterSecret);
// if (string.IsNullOrEmpty(context.Request["oauth_token"]))
// {
// var callbackAddress = new UriBuilder(RedirectUri)
// {
// Query = "state=" + HttpUtility.UrlEncode(context.Request.GetUrlRewriter().AbsoluteUri)
// };
// var reqToken = OAuthUtility.GetRequestToken(TwitterKey, TwitterSecret, callbackAddress.ToString());
// var url = OAuthUtility.BuildAuthorizationUri(reqToken.Token).ToString();
// context.Response.Redirect(url, true);
// var authenticationRequestId = Guid.NewGuid().ToString();
// // Add the user identifier as a query parameters that will be received by `ValidateTwitterAuth`
// var redirectURL = _myAuthRequestStore.AppendAuthenticationRequestIdToCallbackUrl(callbackAddress.ToString(), authenticationRequestId);
// // Initialize the authentication process
// var authenticationRequestToken = appClient.Auth.RequestAuthenticationUrlAsync(redirectURL)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult();
// // Store the token information in the store
// _myAuthRequestStore.AddAuthenticationTokenAsync(authenticationRequestId, authenticationRequestToken)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult();
// context.Response.Redirect(authenticationRequestToken.AuthorizationURL, true);
// return null;
// }
// var requestToken = context.Request.Query["oauth_token"];
// var pin = context.Request.Query["oauth_verifier"];
// // Extract the information from the redirection url
// var requestParameters = RequestCredentialsParameters.FromCallbackUrlAsync(context.Request.RawUrl, _myAuthRequestStore).GetAwaiter().GetResult();
// // Request Twitter to generate the credentials.
// var userCreds = appClient.Auth.RequestCredentialsAsync(requestParameters)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult();
// var tokens = OAuthUtility.GetAccessToken(TwitterKey, TwitterSecret, requestToken, pin);
// // Congratulations the user is now authenticated!
// var userClient = new TwitterClient(userCreds);
// var accesstoken = new OAuthTokens
// {
// AccessToken = tokens.Token,
// AccessTokenSecret = tokens.TokenSecret,
// ConsumerKey = TwitterKey,
// ConsumerSecret = TwitterSecret
// };
// var user = userClient.Users.GetAuthenticatedUserAsync()
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult();
// var userSettings = userClient.AccountSettings.GetAccountSettingsAsync()
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult();
// return user == null
// ? null
// : new LoginProfile
// {
// Name = user.Name,
// DisplayName = user.ScreenName,
// Avatar = user.ProfileImageUrl,
// Locale = userSettings.Language.ToString(),
// Id = user.Id.ToString(CultureInfo.InvariantCulture),
// Provider = ProviderConstants.Twitter
// };
// var account = TwitterAccount.VerifyCredentials(accesstoken).ResponseObject;
// return ProfileFromTwitter(account);
// }
// protected override OAuth20Token Auth(HttpContext context, string scopes, Dictionary<string, string> additional = null)
@ -102,20 +150,18 @@
// throw new NotImplementedException();
// }
// internal static LoginProfile ProfileFromTwitter(TwitterUser twitterUser)
// internal static LoginProfile ProfileFromTwitter(string twitterProfile)
// {
// return twitterUser == null
// ? null
// : new LoginProfile
// {
// Name = twitterUser.Name,
// DisplayName = twitterUser.ScreenName,
// Avatar = twitterUser.ProfileImageSecureLocation,
// TimeZone = twitterUser.TimeZone,
// Locale = twitterUser.Language,
// Id = twitterUser.Id.ToString(CultureInfo.InvariantCulture),
// Provider = ProviderConstants.Twitter
// };
// var jProfile = JObject.Parse(twitterProfile);
// if (jProfile == null) throw new Exception("Failed to correctly process the response");
// return new LoginProfile
// {
// DisplayName = jProfile.Value<string>("name"),
// Locale = jProfile.Value<string>("lang"),
// Id = jProfile.Value<string>("id"),
// Provider = ProviderConstants.Twitter
// };
// }
// }
//}

View File

@ -1,66 +0,0 @@
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
namespace ASC.FederatedLogin.LoginProviders;
[Scope]
public class YahooLoginProvider : BaseLoginProvider<YahooLoginProvider>
{
public const string YahooUrlUserGuid = "https://social.yahooapis.com/v1/me/guid";
public const string YahooUrlContactsFormat = "https://social.yahooapis.com/v1/user/{0}/contacts";
public override string CodeUrl => "https://api.login.yahoo.com/oauth2/request_auth";
public override string AccessTokenUrl => "https://api.login.yahoo.com/oauth2/get_token";
public override string RedirectUri => this["yahooRedirectUrl"];
public override string ClientID => this["yahooClientId"];
public override string ClientSecret => this["yahooClientSecret"];
public override string Scopes => "sdct-r";
public YahooLoginProvider() { }
public YahooLoginProvider(
OAuth20TokenHelper oAuth20TokenHelper,
TenantManager tenantManager,
CoreBaseSettings coreBaseSettings,
CoreSettings coreSettings,
IConfiguration configuration,
ICacheNotify<ConsumerCacheItem> cache,
ConsumerFactory consumerFactory,
Signature signature,
InstanceCrypto instanceCrypto,
string name, int order, Dictionary<string, string> props, Dictionary<string, string> additional = null)
: base(oAuth20TokenHelper, tenantManager, coreBaseSettings, coreSettings, configuration, cache, consumerFactory, signature, instanceCrypto, name, order, props, additional) { }
public OAuth20Token Auth(HttpContext context)
{
return Auth(context, Scopes, out var _);
}
public override LoginProfile GetLoginProfile(string accessToken)
{
throw new NotImplementedException();
}
}

View File

@ -279,16 +279,7 @@ public class LoginProfile
var key = HashHelper.MD5(Transport());
memoryCache.Set(key, this, TimeSpan.FromMinutes(15));
return AppendQueryParam(uri, QuerySessionParamName, key);
}
internal Uri AppendSessionProfile(Uri uri, HttpContext context)
{
//gen key
var key = HashHelper.MD5(Transport());
context.Session.SetString(key, JsonConvert.SerializeObject(this));
return AppendQueryParam(uri, QuerySessionParamName, key);
return AppendQueryParam(uri, QueryCacheParamName, key);
}
internal void ParseFromUrl(HttpContext context, Uri uri, IMemoryCache memoryCache)
@ -298,10 +289,6 @@ public class LoginProfile
{
FromTransport(queryString[QueryParamName]);
}
else if (!string.IsNullOrEmpty(queryString[QuerySessionParamName]))
{
FromTransport(context.Session.GetString(queryString[QuerySessionParamName]));
}
else if (!string.IsNullOrEmpty(queryString[QueryCacheParamName]))
{
FromTransport((string)memoryCache.Get(queryString[QueryCacheParamName]));

View File

@ -33,11 +33,6 @@ public static class LoginProfileExtensions
return profile.AppendProfile(uri);
}
public static Uri AddProfileSession(this Uri uri, LoginProfile profile, HttpContext context)
{
return profile.AppendSessionProfile(uri, context);
}
public static Uri AddProfileCache(this Uri uri, LoginProfile profile, IMemoryCache memoryCache)
{
return profile.AppendCacheProfile(uri, memoryCache);
@ -58,7 +53,7 @@ public static class LoginProfileExtensions
}
if (!string.IsNullOrEmpty(queryString[LoginProfile.QueryCacheParamName]))
{
return (LoginProfile)memoryCache.Get(queryString[LoginProfile.QuerySessionParamName]);
return (LoginProfile)memoryCache.Get(queryString[LoginProfile.QueryCacheParamName]);
}
return null;

View File

@ -31,7 +31,6 @@ public static class ProviderConstants
public const string Twitter = "twitter";
public const string Facebook = "facebook";
public const string LinkedIn = "linkedin";
public const string OpenId = "openid";
public const string Box = "box";
public const string Google = "google";
public const string Yandex = "yandex";

View File

@ -33,6 +33,11 @@ module.exports = (app, config, logger) => {
})
: [];
if(req.originalUrl =="/isLife") {
res.sendStatus(200);
return;
}
if (!foundRoutes.length) {
logger.error(`invalid route ${req.originalUrl}`);
return res.redirect(urlResolver.getPortal404Url(req));

View File

@ -774,7 +774,7 @@ module.exports = function (app, config, logger) {
* @desc Route to load metadata
*/
app.post(config.routes.loadmetadata, onLoadMetadata);
app.get('/isLife', (req, res) => { res.sendStatus(200); });
/**
* @desc Catch any untracked routes
* @param {object} req - request with data parameter (NameID required)

View File

@ -32,6 +32,9 @@ const filenamify = require('filenamify-url');
const webshot = require('./webshot/webshot');
const config = require('../config');
const log = require('./log.js');
const fetch = require("node-fetch");
const dns = require("dns");
const Address = require("ipaddr.js");
const linkReg = /http(s)?:\/\/.*/;
let urls = [];
@ -46,6 +49,29 @@ const cache = new nodeCache({
useClones: false
});
function checkValidUrl(url) {
return new Promise((resolve, reject) => {
fetch(url)
.then(res => {
var host = new URL(res.url).host;
dns.lookup(host, (err, ip, family) => {
if (err) {
log.error(error);
resolve(false);
return;
}
const address = Address.parse(ip);
const range = address.range();
resolve(range === 'unicast');
});
})
.catch(error => {
log.error(error);
resolve(false);
});
});
}
function checkFileExist(pathToFile) {
return new Promise((resolve, reject) => {
fs.stat(pathToFile,
@ -109,7 +135,13 @@ module.exports = function (req, res) {
res.sendFile(pathToFile);
}
co(function* () {
co(function* () {
const isValidUrl = yield checkValidUrl(url);
if (!isValidUrl) {
res.sendFile(noThumb);
return;
}
const exists = yield checkFileExist(pathToFile);
if (exists) {
success();

View File

@ -14,9 +14,11 @@
"express": "^4.17.1",
"filenamify-url": "^2.1.2",
"graceful-fs": "~4.2.4",
"ipaddr.js": "^2.0.1",
"moment": "^2.26.0",
"nconf": "^0.11.3",
"node-cache": "^5.1.0",
"node-fetch": "2.6.1",
"phantomjs-prebuilt": "^2.1.16",
"request": "^2.88.2",
"tmp": "~0.2.1",

File diff suppressed because it is too large Load Diff