|
using Autofac; |
|
using Autofac.Extensions.DependencyInjection; |
|
using AutoMapper; |
|
using Castle.DynamicProxy; |
|
using metasonic.Universal.BusinessLayer.ServiceContracts; |
|
using metasonic.Universal.BusinessLayer.Services; |
|
using metasonic.Universal.Common; |
|
using metasonic.Universal.Common.Helper; |
|
using metasonic.Universal.Common.Resources; |
|
using metasonic.Universal.Constant.Const; |
|
using metasonic.Universal.Constant.Enums; |
|
using metasonic.Universal.Context; |
|
using metasonic.Universal.Doc; |
|
using metasonic.Universal.Doc.Helper; |
|
using metasonic.Universal.Doc.Interface; |
|
using metasonic.Universal.Integrator.Autofac; |
|
using metasonic.Universal.Integrator.Filters; |
|
using metasonic.Universal.Integrator.Helper; |
|
using metasonic.Universal.Integrator.Helper.Cache; |
|
using metasonic.Universal.Integrator.Middleware; |
|
using Microsoft.AspNetCore.Authentication.JwtBearer; |
|
using Microsoft.AspNetCore.Builder; |
|
using Microsoft.AspNetCore.Hosting; |
|
using Microsoft.AspNetCore.Http; |
|
using Microsoft.EntityFrameworkCore; |
|
using Microsoft.Extensions.Configuration; |
|
using Microsoft.Extensions.DependencyInjection; |
|
using Microsoft.Extensions.Hosting; |
|
using Microsoft.Extensions.Primitives; |
|
using Microsoft.IdentityModel.Tokens; |
|
using Newtonsoft.Json; |
|
using Newtonsoft.Json.Linq; |
|
using Serilog; |
|
using System; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Threading; |
|
using Hjson; |
|
//SAML |
|
using ITfoxtec.Identity.Saml2; |
|
using ITfoxtec.Identity.Saml2.Schemas.Metadata; |
|
using ITfoxtec.Identity.Saml2.MvcCore.Configuration; |
|
using metasonic.Universal.ExternalStorage; |
|
using Microsoft.AspNetCore.Http.Features; |
|
using metasonic.Universal.Process.Helper; |
|
//END SAML |
|
|
|
namespace metasonic.Universal.Integrator |
|
{ |
|
public class Startup |
|
{ |
|
public Startup(IConfiguration Configuration) |
|
{ |
|
this.Configuration = Configuration; |
|
} |
|
|
|
public IConfiguration Configuration { get; set; } |
|
|
|
public ILifetimeScope AutofacContainer { get; private set; } |
|
|
|
private byte[] _appsettingsHash { get; set; } |
|
|
|
private int dbType { get; set; } |
|
|
|
// This method gets called by the runtime. Use this method to add services to the container. |
|
public void ConfigureServices(IServiceCollection services) |
|
{ |
|
var logConfiguration = new LoggerConfiguration().ReadFrom.Configuration(Configuration); |
|
|
|
Log.Logger = logConfiguration.CreateLogger(); |
|
services.AddSingleton(Log.Logger); |
|
services.AddSwaggerGen(c => |
|
{ |
|
c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo |
|
{ |
|
Version = "v1", |
|
Title = "Universal Integrator Service", |
|
}); |
|
c.CustomSchemaIds(type => type.ToString()); |
|
}); |
|
|
|
services.AddSingleton<LocalizeManager>(); |
|
services.AddLocalization(options => options.ResourcesPath = "Resources"); |
|
|
|
|
|
services.AddSingleton(provider => new MapperConfiguration(cfg => |
|
{ |
|
cfg.AddProfile(new EntityDTOMapper(provider.GetService<LocalizeManager>())); |
|
}).CreateMapper()); |
|
|
|
//SAML |
|
if (Configuration["enableSAMLAuthentication"].ToLower() == "true") |
|
{ |
|
services.Configure<Saml2Configuration>(Configuration.GetSection("Saml2")); |
|
|
|
services.Configure<Saml2Configuration>(saml2Configuration => |
|
{ |
|
saml2Configuration.AllowedAudienceUris.Add(saml2Configuration.Issuer); |
|
try |
|
{ |
|
var entityDescriptor = new EntityDescriptor(); |
|
entityDescriptor.ReadIdPSsoDescriptorFromUrl(new Uri(Configuration["Saml2:IdPMetadata"])); |
|
if (entityDescriptor.IdPSsoDescriptor != null) |
|
{ |
|
saml2Configuration.SingleSignOnDestination = entityDescriptor.IdPSsoDescriptor.SingleSignOnServices.First().Location; |
|
//saml2Configuration.SingleLogoutDestination = entityDescriptor.IdPSsoDescriptor.SingleLogoutServices.First().Location; |
|
saml2Configuration.SignatureValidationCertificates.AddRange(entityDescriptor.IdPSsoDescriptor.SigningCertificates); |
|
} |
|
else |
|
{ |
|
throw new Exception("IdPSsoDescriptor not loaded from metadata."); |
|
} |
|
} |
|
catch(Exception) |
|
{ |
|
|
|
} |
|
}); |
|
|
|
services.AddSaml2(); |
|
} |
|
//END SAML |
|
|
|
services.AddAuthentication(x => |
|
{ |
|
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; |
|
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; |
|
}) |
|
.AddJwtBearer(options => |
|
{ |
|
options.TokenValidationParameters = new TokenValidationParameters |
|
{ |
|
ValidateIssuer = false, |
|
ValidateAudience = false, |
|
ValidateIssuerSigningKey = true, |
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration[AppConstant.JWK_SECURITYKEY])) |
|
}; |
|
}); |
|
services.AddAuthorization(option => |
|
{ |
|
option.AddPolicy("AdminRole", policy => policy.RequireClaim("IsAdmin", "1")); |
|
option.AddPolicy("SuperUserRole", policy => policy.RequireClaim("IsSuperUser", "1")); |
|
}); |
|
services.AddMemoryCache(); |
|
services.AddControllers(); |
|
services.AddSingleton<AppConfigSetting>(); |
|
services.AddSingleton<ICacheHelper, MemoryCacheHelper>(); |
|
services.AddScoped<IDocLoginService, DocLoginService>(); |
|
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); |
|
services.AddScoped<IUserContext, UserContext>(); |
|
services.AddScoped<CommonHelper>(); |
|
services.AddScoped<PermissionHelper>(); |
|
services.AddScoped<AuthenticationHelper>(); |
|
services.AddScoped<GlobalSearchService>(); |
|
services.AddScoped<RestAPIHelper>(); |
|
services.AddScoped<ProcessApiWebService>(); |
|
services.AddScoped<DocumentTileHelper>(); |
|
services.AddScoped<SharepointStorage>(); |
|
services.AddControllers(); |
|
|
|
var ConnectionStringEncrypted = Convert.ToBoolean(Configuration[AppConstant.IS_CONNECTIONSTRING_ENCRYPTED]); |
|
var ConnectionString = Configuration.GetConnectionString(AppConstant.UNIVERSAL_CONNECTIONSTRING); |
|
ConnectionString = ConnectionStringEncrypted ? EncryptionHelper.Decrypt(ConnectionString, AppConstant.ENCRYPT_DECRYPT_CONN_STRING_KEY) : ConnectionString; |
|
dbType = Convert.ToInt16(Configuration[AppConstant.DBType]); |
|
if (dbType == (int)DBType.SQL) |
|
{ |
|
services.AddDbContext<UniversalClientDbContext>(options => |
|
{ |
|
options.UseSqlServer(ConnectionString, b => b.MigrationsAssembly("metasonic.Universal.Integrator")); |
|
}, ServiceLifetime.Transient |
|
); |
|
} |
|
else if (dbType == (int)DBType.Oracle) |
|
{ |
|
services.AddDbContext<UniversalClientDbContext>(options => |
|
{ |
|
options.UseOracle(ConnectionString, |
|
b => b.MigrationsAssembly("metasonic.Universal.Integrator").UseOracleSQLCompatibility("11")); |
|
}, ServiceLifetime.Transient |
|
); |
|
} |
|
else if (dbType == (int)DBType.Postgre) |
|
{ |
|
services.AddDbContext<UniversalClientDbContext>(options => |
|
{ |
|
options.UseNpgsql(ConnectionString, b => b.MigrationsAssembly("metasonic.Universal.Integrator")); |
|
}, ServiceLifetime.Transient ); |
|
} |
|
DocUrlHelper.DisableSignatureValidation = string.IsNullOrEmpty(Configuration[AppConstant.DISABLE_SIGNATURE_VALIDATION_KEY]) ? true : Configuration[AppConstant.DISABLE_SIGNATURE_VALIDATION_KEY].ToUpper() == "TRUE"; |
|
|
|
services.AddScoped<IInterceptor, DocInterceptor>(); |
|
services.AddScoped<IUnitOfWork, UnitOfWork>(); |
|
services.AddScoped<IUserTilePreferenceService, UserTilePreferenceService>(); |
|
services.AddScoped<IUserService, UserService>(); |
|
services.AddScoped<IRolePermissionService, RolePermissionService>(); |
|
services.AddScoped<IMenuService, MenuService>(); |
|
services.AddScoped<IModuleService, ModuleService>(); |
|
services.AddScoped<IConfigurationService, ConfigurationService>(); |
|
services.AddScoped<IDocumentSharePointService, DocumentSharePointService>(); |
|
services.AddScoped<IUserTileConfigurationService, UserTileConfigurationService>(); |
|
services.AddScoped<IUserSearchSettingService, UserSearchSettingService>(); |
|
services.AddScoped<IUserPreferenceService, UserPreferenceService>(); |
|
services.AddScoped<IUserGridPreferenceService, UserGridPreferenceService>(); |
|
services.AddScoped<IUserDashboardService, UserDashboardService>(); |
|
services.AddScoped<ITileTypeFactory, TileTypeFactory>(); |
|
services.AddScoped<IUserPinnedItemService, UserPinnedItemService>(); |
|
services.AddScoped<IRoleService, RoleService>(); |
|
services.AddScoped<IMasterChildSettingService, MasterChildSettingService>(); |
|
services.AddScoped<IUserSearchCriteriaService, UserSearchCriteriaService>(); |
|
services.AddScoped<IInboxSettingService, InboxSettingService>(); |
|
services.AddScoped<IDocumentTemplateSettingService, DocumentTemplateSettingService>(); |
|
services.AddScoped<IPublicFilterService, PublicFilterService>(); |
|
services.AddOptions(); |
|
services.AddCors(options => |
|
{ |
|
options.AddPolicy("AllowEverything", builder => |
|
{ |
|
builder.AllowAnyOrigin() |
|
.AllowAnyMethod() |
|
.AllowAnyHeader(); |
|
}); |
|
}); |
|
|
|
services.AddScoped<ConfigurationOnActionFilter>(); |
|
services.Configure<FormOptions>(x => |
|
{ |
|
x.ValueLengthLimit = int.MaxValue; |
|
x.MultipartBodyLengthLimit = int.MaxValue; |
|
x.MultipartHeadersLengthLimit = int.MaxValue; |
|
}); |
|
//CreateGraphClient(); |
|
} |
|
|
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. |
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
|
{ |
|
this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); |
|
if (dbType == (int)DBType.Postgre) |
|
{ |
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); |
|
} |
|
if (env.IsDevelopment()) |
|
{ |
|
app.UseDeveloperExceptionPage(); |
|
} |
|
|
|
app.UseRouting(); |
|
app.UseCors("AllowEverything"); |
|
app.UseAuthentication(); |
|
app.UseSaml2(); //SAML |
|
app.UseAuthorization(); |
|
app.UseMiddleware(typeof(ErrorHandlingMiddleware)); |
|
app.UseMiddleware<JwtMiddleware>(); |
|
|
|
var serviceProvider = app.ApplicationServices; |
|
var cacheHelper = serviceProvider.GetService<ICacheHelper>(); |
|
string systemCulture = System.Globalization.CultureInfo.CurrentCulture.Name; |
|
cacheHelper.Put(AppConstant.SYSTEM_CULTURE, systemCulture); |
|
//cacheHelper.Put(AppConstant.SYSTEM_CULTURE, "de-DE"); |
|
|
|
Log.Logger.Information("StartUp -> Current system culture is :" + systemCulture); |
|
|
|
app.Use(async (context, next) => |
|
{ |
|
context.Response.Headers.Add("Access-Control-Max-Age", "600"); |
|
var userLangs = context.Request.Headers["CultureInfo"].ToString(); |
|
var firstLang = userLangs.Split(',').FirstOrDefault(); |
|
|
|
var lang = "en-US"; //default |
|
switch (firstLang) |
|
{ |
|
case "de-DE": |
|
lang = "de-DE"; //allowed |
|
break; |
|
} |
|
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(lang); |
|
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; |
|
|
|
await next.Invoke(); |
|
}); |
|
|
|
app.UseEndpoints(endpoints => |
|
{ |
|
endpoints.MapControllers(); |
|
}); |
|
|
|
app.UseSwagger(); |
|
app.UseSwaggerUI(c => |
|
{ |
|
c.SwaggerEndpoint("../swagger/v1/swagger.json", "Universal Client Integrator API"); |
|
}); |
|
|
|
MapperHelper.Mapper = serviceProvider.GetService<IMapper>(); |
|
EncryptConnectionStrings(env); |
|
_appsettingsHash = EncryptionHelper.ComputeHash(AppConstant.APPSETTING_FILE); |
|
ChangeToken.OnChange( |
|
() => Configuration.GetReloadToken(), |
|
(state) => InvokeConfigurationChanged(state), |
|
env); |
|
} |
|
|
|
private void InvokeConfigurationChanged(IWebHostEnvironment env) |
|
{ |
|
Log.Information("App settings changes detected."); |
|
|
|
byte[] appsettingsHash = EncryptionHelper.ComputeHash(AppConstant.APPSETTING_FILE); |
|
|
|
if (!_appsettingsHash.SequenceEqual(appsettingsHash)) |
|
{ |
|
_appsettingsHash = appsettingsHash; |
|
|
|
EncryptConnectionStrings(env, true); |
|
Log.Information("Configuration is reloaded."); |
|
} |
|
} |
|
|
|
public void ConfigureContainer(ContainerBuilder builder) |
|
{ |
|
builder.RegisterModule(new AutofacModule()); |
|
} |
|
|
|
#region private methods |
|
/// <summary> |
|
/// Encrypt Connection Strings |
|
/// </summary> |
|
/// <param name="env"></param> |
|
private void EncryptConnectionStrings(IWebHostEnvironment env, bool isAppSettingChange = false) |
|
{ |
|
try |
|
{ |
|
var appSettingFilePhysicalPath = env.ContentRootFileProvider.GetFileInfo(AppConstant.APPSETTING_FILE).PhysicalPath; |
|
var appSettingsObject = (WscJsonObject)HjsonValue.Load(appSettingFilePhysicalPath, new HjsonOptions() { KeepWsc = true }).Qo(); |
|
bool isConnectionStringChange = false; |
|
var IsConnectionStringEncrypted = string.IsNullOrEmpty(appSettingsObject.Qstr(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED)) ? false : Convert.ToBoolean(appSettingsObject.Qstr(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED)); |
|
if (!IsConnectionStringEncrypted) |
|
{ |
|
if (!isAppSettingChange) |
|
{ |
|
var connectionSection = appSettingsObject.Qo(AppConstant.CONNECTION_STRING); |
|
if (connectionSection != null && connectionSection.TryGetValue(AppConstant.UNIVERSAL_CONNECTIONSTRING, out JsonValue connString)) |
|
{ |
|
int isConnectionStringEncryptKeyIndex = appSettingsObject.Order.IndexOf(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED); |
|
connectionSection[AppConstant.UNIVERSAL_CONNECTIONSTRING] = EncryptionHelper.Encrypt(connString, AppConstant.ENCRYPT_DECRYPT_CONN_STRING_KEY); |
|
appSettingsObject[AppConstant.IS_CONNECTIONSTRING_ENCRYPTED] = true; |
|
if (isConnectionStringEncryptKeyIndex == -1) |
|
{ |
|
insertKeyIntoAppSettingFile(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED, AppConstant.CONNECTION_STRING, appSettingsObject); |
|
} |
|
isConnectionStringChange = true; |
|
} |
|
} |
|
else |
|
{ |
|
Log.Information("Connection string changes is applied only after application restart. Please restart the application if you want to change application connection string."); |
|
} |
|
} |
|
bool anyIncomingEmailChanges = EncyptIncomingEmailConnectionStrings(appSettingsObject); |
|
//bool isSharepointConfigurationChange = EncryptSharepointConfiguration(appSettingsObject); |
|
if (isConnectionStringChange || anyIncomingEmailChanges) |
|
{ |
|
SaveAppSettingChanges(appSettingFilePhysicalPath, appSettingsObject); |
|
} |
|
} |
|
catch (Exception ex) |
|
{ |
|
Log.Error(ex, ex.Message); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Save AppSetting changes |
|
/// </summary> |
|
/// <param name="appSettingFilePhysicalPath"></param> |
|
/// <param name="appSettingsObject"></param> |
|
private void SaveAppSettingChanges(string appSettingFilePhysicalPath, WscJsonObject appSettingsObject) |
|
{ |
|
HjsonValue.Save(appSettingsObject, appSettingFilePhysicalPath, new HjsonOptions() { KeepWsc = true }); |
|
} |
|
|
|
/// <summary> |
|
/// Add new key IsConnectionStringEncrypted into appConfig file |
|
/// </summary> |
|
/// <param name="key"></param> |
|
/// <param name="wscJsonObject"></param> |
|
private void insertKeyIntoAppSettingFile(string newKey, string placementKey, WscJsonObject wscJsonObject) |
|
{ |
|
int connectionStringSectionindex = wscJsonObject.Order.IndexOf(placementKey) + 1; |
|
wscJsonObject.Order.Insert(connectionStringSectionindex, newKey); |
|
} |
|
private bool EncyptIncomingEmailConnectionStrings(WscJsonObject appSettingsObject) |
|
{ |
|
bool anyChanges = false; |
|
var incomingEmailObj = (WscJsonObject)appSettingsObject.Qo(AppConstant.INCOMING_EMAIL_CONFIG); |
|
if (incomingEmailObj != null && incomingEmailObj.Keys.Count > 0) |
|
{ |
|
for (int i = 0; i < incomingEmailObj.Keys.Count; i++) |
|
{ |
|
var key = incomingEmailObj.Keys.ToArray()[i]; |
|
var configObj = (WscJsonObject)incomingEmailObj.Qo(key); |
|
var IsConnectionStringEncrypted = string.IsNullOrEmpty(configObj.Qstr(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED)) ? false : |
|
Convert.ToBoolean(configObj.Qstr(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED)); |
|
var connectionString = configObj.Qstr(AppConstant.INCOMINGE_MAIL_CONNECTIONSTRING); |
|
if (!IsConnectionStringEncrypted && !string.IsNullOrEmpty(connectionString)) |
|
{ |
|
configObj[AppConstant.INCOMINGE_MAIL_CONNECTIONSTRING] = EncryptionHelper.Encrypt(connectionString, AppConstant.ENCRYPT_DECRYPT_CONN_STRING_KEY); |
|
int isConnectionStringEncryptKeyIndex = configObj.Order.IndexOf(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED); |
|
configObj[AppConstant.IS_CONNECTIONSTRING_ENCRYPTED] = true; |
|
if (isConnectionStringEncryptKeyIndex == -1) |
|
{ |
|
insertKeyIntoAppSettingFile(AppConstant.IS_CONNECTIONSTRING_ENCRYPTED, AppConstant.INCOMINGE_MAIL_CONNECTIONSTRING, configObj); |
|
} |
|
anyChanges = true; |
|
} |
|
} |
|
} |
|
return anyChanges; |
|
} |
|
|
|
//private void CreateGraphClient() |
|
//{ |
|
// string ClientId = "cd771206-9370-437d-afc2-041890b2630c"; |
|
// string TenantId = "18fdbfe4-3a43-45f2-912f-6a88c76f7c49"; |
|
// string[] Scopes = new String[1] { "https://graph.microsoft.com/.default" }; |
|
|
|
|
|
// string password = "system123#"; |
|
// SecureString securePassword = new SecureString(); |
|
// foreach (char c in password) |
|
// { securePassword.AppendChar(c); } |
|
|
|
// IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder |
|
// .Create(ClientId) |
|
// .WithTenantId(TenantId) |
|
// .Build(); |
|
|
|
// UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, Scopes); |
|
|
|
// GraphServiceClient graphClient = new GraphServiceClient(authProvider); |
|
|
|
// User me = graphClient.Me.Request() |
|
// .WithUsernamePassword("[email protected]", securePassword) |
|
// .GetAsync().Result; |
|
|
|
// EncryptionHelper.graphClient = graphClient; |
|
//} |
|
#endregion private methods |
|
} |
|
} |