[WIP] IT
This commit is contained in:
parent
aa637141b5
commit
a5dfc55d5e
11 changed files with 527 additions and 14 deletions
32
cli/src/Authentication/AuthException.cs
Normal file
32
cli/src/Authentication/AuthException.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace cli.Authentication
|
||||
{
|
||||
[Serializable]
|
||||
internal class AuthException : Exception
|
||||
{
|
||||
private object p;
|
||||
|
||||
public AuthException()
|
||||
{
|
||||
}
|
||||
|
||||
public AuthException(object p)
|
||||
{
|
||||
this.p = p;
|
||||
}
|
||||
|
||||
public AuthException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public AuthException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
}
|
||||
|
||||
protected AuthException(SerializationInfo info, StreamingContext context) : base(info, context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
475
cli/src/Authentication/OAuthenticator.cs
Normal file
475
cli/src/Authentication/OAuthenticator.cs
Normal file
|
|
@ -0,0 +1,475 @@
|
|||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using GetUsernameAsyncFunc=System.Func<System.Collections.Generic.IDictionary<string,string>, System.Threading.Tasks.Task<string>>;
|
||||
using System.IO;
|
||||
using Microsoft.Extensions.WebEncoders;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace cli.Authentication
|
||||
{
|
||||
public class OAuthenticator
|
||||
{
|
||||
UrlEncoder _urlEncoder;
|
||||
public OAuthenticator(UrlEncoder urlEncoder)
|
||||
{
|
||||
_urlEncoder = urlEncoder;
|
||||
|
||||
}
|
||||
string clientId;
|
||||
string clientSecret;
|
||||
string scope;
|
||||
Uri authorizeUrl;
|
||||
Uri accessTokenUrl;
|
||||
Uri redirectUrl;
|
||||
GetUsernameAsyncFunc getUsernameAsync;
|
||||
|
||||
string requestState;
|
||||
bool reportedForgery = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client identifier.
|
||||
/// </summary>
|
||||
/// <value>The client identifier.</value>
|
||||
public string ClientId
|
||||
{
|
||||
get { return this.clientId; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the client secret.
|
||||
/// </summary>
|
||||
/// <value>The client secret.</value>
|
||||
public string ClientSecret
|
||||
{
|
||||
get { return this.clientSecret; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorization scope.
|
||||
/// </summary>
|
||||
/// <value>The authorization scope.</value>
|
||||
public string Scope
|
||||
{
|
||||
get { return this.scope; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the authorize URL.
|
||||
/// </summary>
|
||||
/// <value>The authorize URL.</value>
|
||||
public Uri AuthorizeUrl
|
||||
{
|
||||
get { return this.authorizeUrl; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the access token URL.
|
||||
/// </summary>
|
||||
/// <value>The URL used to request access tokens after an authorization code was received.</value>
|
||||
public Uri AccessTokenUrl
|
||||
{
|
||||
get { return this.accessTokenUrl; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redirect Url
|
||||
/// </summary>
|
||||
public Uri RedirectUrl
|
||||
{
|
||||
get { return this.redirectUrl; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="ZicMoove.Droid.OAuth.YaOAuth2WebAuthenticator"/>
|
||||
/// that authenticates using implicit granting (token).
|
||||
/// </summary>
|
||||
/// <param name='clientId'>
|
||||
/// Client identifier.
|
||||
/// </param>
|
||||
/// <param name='scope'>
|
||||
/// Authorization scope.
|
||||
/// </param>
|
||||
/// <param name='authorizeUrl'>
|
||||
/// Authorize URL.
|
||||
/// </param>
|
||||
/// <param name='redirectUrl'>
|
||||
/// Redirect URL.
|
||||
/// </param>
|
||||
/// <param name='getUsernameAsync'>
|
||||
/// Method used to fetch the username of an account
|
||||
/// after it has been successfully authenticated.
|
||||
/// </param>
|
||||
public OAuthenticator(string clientId, string scope, Uri authorizeUrl, Uri redirectUrl, GetUsernameAsyncFunc getUsernameAsync = null)
|
||||
: this(redirectUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(clientId))
|
||||
{
|
||||
throw new ArgumentException("clientId must be provided", "clientId");
|
||||
}
|
||||
if (authorizeUrl==null)
|
||||
throw new ArgumentNullException("authorizeUrl");
|
||||
|
||||
this.clientId = clientId;
|
||||
this.scope = scope ?? "";
|
||||
this.authorizeUrl = authorizeUrl ;
|
||||
this.getUsernameAsync = getUsernameAsync;
|
||||
this.accessTokenUrl = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance <see cref="ZicMoove.Droid.OAuth.YaOAuth2WebAuthenticator"/>
|
||||
/// that authenticates using authorization codes (code).
|
||||
/// </summary>
|
||||
/// <param name='clientId'>
|
||||
/// Client identifier.
|
||||
/// </param>
|
||||
/// <param name='clientSecret'>
|
||||
/// Client secret.
|
||||
/// </param>
|
||||
/// <param name='scope'>
|
||||
/// Authorization scope.
|
||||
/// </param>
|
||||
/// <param name='authorizeUrl'>
|
||||
/// Authorize URL.
|
||||
/// </param>
|
||||
/// <param name='redirectUrl'>
|
||||
/// Redirect URL.
|
||||
/// </param>
|
||||
/// <param name='accessTokenUrl'>
|
||||
/// URL used to request access tokens after an authorization code was received.
|
||||
/// </param>
|
||||
/// <param name='getUsernameAsync'>
|
||||
/// Method used to fetch the username of an account
|
||||
/// after it has been successfully authenticated.
|
||||
/// </param>
|
||||
public OAuthenticator(string clientId, string clientSecret, string scope, Uri authorizeUrl, Uri redirectUrl, Uri accessTokenUrl, GetUsernameAsyncFunc getUsernameAsync = null)
|
||||
: this(redirectUrl, clientSecret, accessTokenUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(clientId))
|
||||
{
|
||||
throw new ArgumentException("clientId must be provided", "clientId");
|
||||
}
|
||||
this.clientId = clientId;
|
||||
|
||||
if (string.IsNullOrEmpty(clientSecret))
|
||||
{
|
||||
throw new ArgumentException("clientSecret must be provided", "clientSecret");
|
||||
}
|
||||
this.clientSecret = clientSecret;
|
||||
|
||||
this.scope = scope ?? "";
|
||||
|
||||
if (authorizeUrl == null)
|
||||
{
|
||||
throw new ArgumentNullException("authorizeUrl");
|
||||
}
|
||||
this.authorizeUrl = authorizeUrl;
|
||||
|
||||
if (accessTokenUrl == null)
|
||||
{
|
||||
throw new ArgumentNullException("accessTokenUrl");
|
||||
}
|
||||
this.accessTokenUrl = accessTokenUrl;
|
||||
|
||||
if (redirectUrl == null)
|
||||
throw new Exception("redirectUrl is null");
|
||||
|
||||
this.redirectUrl = redirectUrl;
|
||||
|
||||
this.getUsernameAsync = getUsernameAsync;
|
||||
}
|
||||
|
||||
|
||||
OAuthenticator(Uri redirectUrl, string clientSecret = null, Uri accessTokenUrl = null)
|
||||
|
||||
{
|
||||
this.redirectUrl = redirectUrl;
|
||||
|
||||
this.clientSecret = clientSecret;
|
||||
|
||||
this.accessTokenUrl = accessTokenUrl;
|
||||
|
||||
//
|
||||
// Generate a unique state string to check for forgeries
|
||||
//
|
||||
var chars = new char[16];
|
||||
var rand = new Random();
|
||||
for (var i = 0; i < chars.Length; i++)
|
||||
{
|
||||
chars[i] = (char)rand.Next((int)'a', (int)'z' + 1);
|
||||
}
|
||||
this.requestState = new string(chars);
|
||||
}
|
||||
|
||||
bool IsImplicit { get { return accessTokenUrl == null; } }
|
||||
|
||||
/// <summary>
|
||||
/// Method that returns the initial URL to be displayed in the web browser.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A task that will return the initial URL.
|
||||
/// </returns>
|
||||
public Task<Uri> GetInitialUrlAsync()
|
||||
{
|
||||
var url = new Uri(string.Format(
|
||||
"{0}?client_id={1}&redirect_uri={2}&response_type={3}&scope={4}&state={5}",
|
||||
authorizeUrl.AbsoluteUri,
|
||||
Uri.EscapeDataString(clientId),
|
||||
Uri.EscapeDataString(RedirectUrl.AbsoluteUri),
|
||||
IsImplicit ? "token" : "code",
|
||||
Uri.EscapeDataString(scope),
|
||||
Uri.EscapeDataString(requestState)));
|
||||
|
||||
var tcs = new TaskCompletionSource<Uri>();
|
||||
tcs.SetResult(url);
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a new page has been loaded.
|
||||
/// </summary>
|
||||
/// <param name='url'>
|
||||
/// URL of the page.
|
||||
/// </param>
|
||||
/// <param name='query'>
|
||||
/// The parsed query of the URL.
|
||||
/// </param>
|
||||
/// <param name='fragment'>
|
||||
/// The parsed fragment of the URL.
|
||||
/// </param>
|
||||
protected void OnPageEncountered(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
|
||||
{
|
||||
|
||||
if (url.AbsoluteUri.StartsWith(this.redirectUrl.AbsoluteUri))
|
||||
{
|
||||
// if (!this.redirectUrl.Equals(url)) {
|
||||
|
||||
// this is not our redirect page,
|
||||
// but perhaps one one the third party identity providers
|
||||
// One don't check for a state here.
|
||||
//
|
||||
/* if (fragment.ContainsKey("continue")) {
|
||||
var cont = fragment["continue"];
|
||||
// TODO continue browsing this address
|
||||
|
||||
var tcs = new TaskCompletionSource<Uri>();
|
||||
tcs.SetResult(new Uri(cont));
|
||||
tcs.Task.RunSynchronously();
|
||||
}
|
||||
return;*/
|
||||
// }
|
||||
|
||||
var all = new Dictionary<string, string>(query);
|
||||
foreach (var kv in fragment)
|
||||
all[kv.Key] = kv.Value;
|
||||
|
||||
//
|
||||
// Check for forgeries
|
||||
//
|
||||
if (all.ContainsKey("state"))
|
||||
{
|
||||
if (all["state"] != requestState && !reportedForgery)
|
||||
{
|
||||
reportedForgery = true;
|
||||
OnError("Invalid state from server. Possible forgery!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnError(string v)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
private void OnError(AggregateException ex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised when a new page has been loaded.
|
||||
/// </summary>
|
||||
/// <param name='url'>
|
||||
/// URL of the page.
|
||||
/// </param>
|
||||
/// <param name='query'>
|
||||
/// The parsed query string of the URL.
|
||||
/// </param>
|
||||
/// <param name='fragment'>
|
||||
/// The parsed fragment of the URL.
|
||||
/// </param>
|
||||
protected void OnRedirectPageLoaded(Uri url, IDictionary<string, string> query, IDictionary<string, string> fragment)
|
||||
{
|
||||
//
|
||||
// Look for the access_token
|
||||
//
|
||||
if (fragment.ContainsKey("access_token"))
|
||||
{
|
||||
//
|
||||
// We found an access_token
|
||||
//
|
||||
OnRetrievedAccountProperties(fragment);
|
||||
}
|
||||
else if (!IsImplicit)
|
||||
{
|
||||
//
|
||||
// Look for the code
|
||||
//
|
||||
if (query.ContainsKey("code"))
|
||||
{
|
||||
var code = query["code"];
|
||||
RequestAccessTokenAsync(code).ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
OnError(task.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnRetrievedAccountProperties(task.Result);
|
||||
}
|
||||
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnError("Expected code in response, but did not receive one.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
OnError("Expected access_token in response, but did not receive one.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously requests an access token with an authorization <paramref name="code"/>.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A dictionary of data returned from the authorization request.
|
||||
/// </returns>
|
||||
/// <param name='code'>The authorization code.</param>
|
||||
/// <remarks>Implements: http://tools.ietf.org/html/rfc6749#section-4.1</remarks>
|
||||
Task<IDictionary<string, string>> RequestAccessTokenAsync(string code)
|
||||
{
|
||||
var queryValues = new Dictionary<string, string> {
|
||||
{ "grant_type", "authorization_code" },
|
||||
{ "code", code },
|
||||
{ "redirect_uri", RedirectUrl.AbsoluteUri },
|
||||
{ "client_id", clientId }
|
||||
};
|
||||
if (!string.IsNullOrEmpty(clientSecret))
|
||||
{
|
||||
queryValues["client_secret"] = clientSecret;
|
||||
}
|
||||
|
||||
return RequestAccessTokenAsync(queryValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously makes a request to the access token URL with the given parameters.
|
||||
/// </summary>
|
||||
/// <param name="queryValues">The parameters to make the request with.</param>
|
||||
/// <returns>The data provided in the response to the access token request.</returns>
|
||||
protected async Task<IDictionary<string, string>> RequestAccessTokenAsync(IDictionary<string, string> queryValues)
|
||||
{
|
||||
StringBuilder postData = new StringBuilder();
|
||||
foreach (string key in queryValues.Keys)
|
||||
{
|
||||
postData.Append(_urlEncoder.UrlEncode($"{key}={queryValues[key]}&"));
|
||||
}
|
||||
var req = WebRequest.Create(accessTokenUrl);
|
||||
(req as HttpWebRequest).Accept = "application/json";
|
||||
req.Method = "POST";
|
||||
var body = Encoding.UTF8.GetBytes(postData.ToString());
|
||||
req.ContentLength = body.Length;
|
||||
req.ContentType = "application/x-www-form-urlencoded";
|
||||
using (var s = req.GetRequestStream())
|
||||
{
|
||||
s.Write(body, 0, body.Length);
|
||||
s.Close();
|
||||
}
|
||||
|
||||
var auth = await req.GetResponseAsync();
|
||||
var repstream = auth.GetResponseStream();
|
||||
|
||||
var respReader = new StreamReader(repstream);
|
||||
|
||||
var text = await respReader.ReadToEndAsync();
|
||||
|
||||
req.Abort();
|
||||
// Parse the response
|
||||
var data = text.Contains("{") ? JsonDecode(text) : FormDecode(text);
|
||||
|
||||
if (data.ContainsKey("error"))
|
||||
{
|
||||
throw new AuthException("Error authenticating: " + data["error"]);
|
||||
}
|
||||
else if (data.ContainsKey("access_token"))
|
||||
{
|
||||
return data;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new AuthException("Expected access_token in access token response, but did not receive one.");
|
||||
}
|
||||
}
|
||||
|
||||
private IDictionary<string,string> FormDecode(string text)
|
||||
{
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private IDictionary<string,string> JsonDecode(string text)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Dictionary<string,string>>(text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that is fired when an access token has been retreived.
|
||||
/// </summary>
|
||||
/// <param name='accountProperties'>
|
||||
/// The retrieved account properties
|
||||
/// </param>
|
||||
protected virtual void OnRetrievedAccountProperties(IDictionary<string, string> accountProperties)
|
||||
{
|
||||
//
|
||||
// Now we just need a username for the account
|
||||
//
|
||||
if (getUsernameAsync != null)
|
||||
{
|
||||
getUsernameAsync(accountProperties).ContinueWith(task =>
|
||||
{
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
OnError(task.Exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnSucceeded(task.Result, accountProperties);
|
||||
}
|
||||
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
else
|
||||
{
|
||||
OnSucceeded("", accountProperties);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSucceeded(string v, IDictionary<string, string> accountProperties)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
30
cli/src/Program.cs
Normal file
30
cli/src/Program.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
// using Microsoft.AspNet.Authorization;
|
||||
// using Microsoft.AspNet.Diagnostics;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using cli.Services;
|
||||
|
||||
namespace cli
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var host = new WebHostBuilder();
|
||||
|
||||
var hostengnine = host
|
||||
.UseEnvironment("Development")
|
||||
.UseServer("cli")
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
|
||||
var app = hostengnine.Start();
|
||||
var mailer = app.Services.GetService<EMailer>();
|
||||
var loggerFactory = app.Services.GetService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger<Program>();
|
||||
mailer.SendMonthlyEmail(1,"UserOrientedTemplate");
|
||||
logger.LogInformation("Finished");
|
||||
}
|
||||
}
|
||||
}
|
||||
188
cli/src/Services/EMailer.cs
Normal file
188
cli/src/Services/EMailer.cs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Emit;
|
||||
using Microsoft.AspNet.Razor;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.AspNet.Identity.EntityFramework;
|
||||
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Templates;
|
||||
using Yavsc.Abstract.Templates;
|
||||
using Yavsc.Services;
|
||||
using Yavsc.Abstract.Manage;
|
||||
|
||||
namespace cli.Services
|
||||
{
|
||||
|
||||
public class EMailer
|
||||
{
|
||||
const string DefaultBaseClassName = "ATemplate";
|
||||
const string DefaultBaseClass = nameof(UserOrientedTemplate);
|
||||
const string DefaultNamespace = "CompiledRazorTemplates";
|
||||
|
||||
RazorTemplateEngine razorEngine;
|
||||
IStringLocalizer<EMailer> stringLocalizer;
|
||||
ILogger logger;
|
||||
ApplicationDbContext dbContext;
|
||||
IEmailSender mailSender;
|
||||
RazorEngineHost host;
|
||||
|
||||
public EMailer(ApplicationDbContext context, IEmailSender sender, IStringLocalizer<EMailer> localizer, ILoggerFactory loggerFactory)
|
||||
{
|
||||
stringLocalizer = localizer;
|
||||
mailSender = sender;
|
||||
|
||||
logger = loggerFactory.CreateLogger<EMailer>();
|
||||
|
||||
var language = new CSharpRazorCodeLanguage();
|
||||
|
||||
host = new RazorEngineHost(language)
|
||||
{
|
||||
DefaultBaseClass = DefaultBaseClass,
|
||||
DefaultClassName = DefaultBaseClassName,
|
||||
DefaultNamespace = DefaultNamespace
|
||||
};
|
||||
|
||||
host.NamespaceImports.Add("System");
|
||||
host.NamespaceImports.Add("Yavsc.Templates");
|
||||
host.NamespaceImports.Add("Yavsc.Models");
|
||||
host.NamespaceImports.Add("Yavsc.Models.Identity");
|
||||
host.NamespaceImports.Add("Microsoft.AspNet.Identity.EntityFramework");
|
||||
host.InstrumentedSourceFilePath = ".";
|
||||
host.StaticHelpers = true;
|
||||
dbContext = context;
|
||||
razorEngine = new RazorTemplateEngine(host);
|
||||
}
|
||||
|
||||
|
||||
public void SendMonthlyEmail(long templateCode, string baseclassName = DefaultBaseClassName)
|
||||
{
|
||||
string className = "Generated" + baseclassName;
|
||||
|
||||
string subtemp = stringLocalizer["MonthlySubjectTemplate"].Value;
|
||||
|
||||
logger.LogInformation($"Generating {subtemp}[{className}]");
|
||||
|
||||
var templateInfo = dbContext.MailingTemplate.FirstOrDefault(t => t.Id == templateCode);
|
||||
|
||||
if (templateInfo==null) throw new Exception($"No template found under id {templateCode}.");
|
||||
logger.LogInformation($"Using code: {templateCode}, subject: {subtemp} ");
|
||||
logger.LogInformation("And body:\n"+templateInfo.Body);
|
||||
using (StringReader reader = new StringReader(templateInfo.Body))
|
||||
{
|
||||
|
||||
// Generate code for the template
|
||||
var razorResult = razorEngine.GenerateCode(reader, className, DefaultNamespace, DefaultNamespace+".cs");
|
||||
|
||||
logger.LogInformation("Razor exited " + (razorResult.Success ? "Ok" : "Ko") + ".");
|
||||
|
||||
logger.LogInformation(razorResult.GeneratedCode);
|
||||
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(razorResult.GeneratedCode);
|
||||
logger.LogInformation("CSharp parsed");
|
||||
List<MetadataReference> references = new List<MetadataReference>();
|
||||
|
||||
foreach (var type in new Type[] {
|
||||
typeof(object),
|
||||
typeof(Enumerable),
|
||||
typeof(IdentityUser),
|
||||
typeof(ApplicationUser),
|
||||
typeof(Template),
|
||||
typeof(UserOrientedTemplate),
|
||||
typeof(System.Threading.Tasks.TaskExtensions)
|
||||
} )
|
||||
{
|
||||
var location = type.Assembly.Location;
|
||||
if (!string.IsNullOrWhiteSpace(location)) {
|
||||
references.Add(
|
||||
MetadataReference.CreateFromFile(location)
|
||||
);
|
||||
logger.LogInformation($"Assembly for {type.Name} found at {location}");
|
||||
} else logger.LogWarning($"Assembly Not found for {type.Name}");
|
||||
}
|
||||
|
||||
logger.LogInformation("Compilation creation ...");
|
||||
|
||||
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
|
||||
.WithAllowUnsafe(true).WithOptimizationLevel(OptimizationLevel.Debug)
|
||||
.WithOutputKind(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.AnyCpu)
|
||||
.WithUsings("Yavsc.Templates")
|
||||
;
|
||||
string assemblyName = DefaultNamespace;
|
||||
CSharpCompilation compilation = CSharpCompilation.Create(
|
||||
assemblyName,
|
||||
syntaxTrees: new[] { syntaxTree },
|
||||
references: references,
|
||||
options: compilationOptions
|
||||
);
|
||||
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
logger.LogInformation("Emitting result ...");
|
||||
EmitResult result = compilation.Emit(ms);
|
||||
foreach (Diagnostic diagnostic in result.Diagnostics.Where(diagnostic =>
|
||||
diagnostic.Severity < DiagnosticSeverity.Error && !diagnostic.IsWarningAsError))
|
||||
{
|
||||
logger.LogWarning("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
|
||||
logger.LogWarning("{0}: {1}", diagnostic.Id, diagnostic.Location.GetLineSpan());
|
||||
}
|
||||
if (!result.Success)
|
||||
{
|
||||
logger.LogInformation(razorResult.GeneratedCode);
|
||||
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
|
||||
diagnostic.IsWarningAsError ||
|
||||
diagnostic.Severity == DiagnosticSeverity.Error);
|
||||
foreach (Diagnostic diagnostic in failures)
|
||||
{
|
||||
logger.LogCritical("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
|
||||
logger.LogCritical("{0}: {1}", diagnostic.Id, diagnostic.Location.GetLineSpan());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation(razorResult.GeneratedCode);
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
Assembly assembly = Assembly.Load(ms.ToArray());
|
||||
|
||||
Type type = assembly.GetType(DefaultNamespace + "." + className);
|
||||
var generatedtemplate = (UserOrientedTemplate) Activator.CreateInstance(type);
|
||||
if (generatedtemplate==null) {
|
||||
logger.LogError("No generated template ... exiting.");
|
||||
throw new InvalidOperationException("No generated template");
|
||||
}
|
||||
foreach (var user in dbContext.ApplicationUser.Where(
|
||||
u => u.AllowMonthlyEmail
|
||||
))
|
||||
{
|
||||
logger.LogInformation("Generation for " + user.UserName);
|
||||
generatedtemplate.Init();
|
||||
generatedtemplate.User = user;
|
||||
generatedtemplate.ExecuteAsync();
|
||||
logger.LogInformation(generatedtemplate.GeneratedText);
|
||||
EmailSentViewModel mailSentInfo = this.mailSender.SendEmailAsync
|
||||
(user.UserName, user.Email, $"monthly email", generatedtemplate.GeneratedText).Result;
|
||||
if (mailSentInfo==null)
|
||||
logger.LogError("No info on sending");
|
||||
else if (!mailSentInfo.Sent)
|
||||
logger.LogError($"{mailSentInfo.ErrorMessage}");
|
||||
else
|
||||
logger.LogInformation($"mailId:{mailSentInfo.MessageId} \nto:{user.UserName}");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
12
cli/src/Services/YaRazorEngineHost.cs
Normal file
12
cli/src/Services/YaRazorEngineHost.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Razor;
|
||||
|
||||
namespace cli
|
||||
{
|
||||
public class YaRazorEngineHost: RazorEngineHost
|
||||
{
|
||||
public YaRazorEngineHost(): base()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
101
cli/src/Startup.cs
Normal file
101
cli/src/Startup.cs
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
using System;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Hosting;
|
||||
using Microsoft.AspNet.Razor;
|
||||
using Microsoft.Extensions.PlatformAbstractions;
|
||||
using cli.Services;
|
||||
using Yavsc;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Services;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.AspNet.Authentication;
|
||||
using Microsoft.Extensions.WebEncoders;
|
||||
|
||||
namespace cli
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public string ConnectionString
|
||||
{
|
||||
get ; set;
|
||||
}
|
||||
|
||||
public static SiteSettings SiteSetup { get; private set; }
|
||||
public static SmtpSettings SmtpSettup { get; private set; }
|
||||
public static IConfiguration Configuration { get; set; }
|
||||
|
||||
public static string HostingFullName { get; private set; }
|
||||
|
||||
ILogger logger;
|
||||
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
|
||||
{
|
||||
var devtag = env.IsDevelopment()?"D":"";
|
||||
var prodtag = env.IsProduction()?"P":"";
|
||||
var stagetag = env.IsStaging()?"S":"";
|
||||
|
||||
HostingFullName = $"{appEnv.RuntimeFramework.FullName} [{env.EnvironmentName}:{prodtag}{devtag}{stagetag}]";
|
||||
// Set up configuration sources.
|
||||
|
||||
var builder = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.AddJsonFile("appsettings.json")
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
Configuration = builder.Build();
|
||||
ConnectionString = Configuration["Data:DefaultConnection:ConnectionString"];
|
||||
AppDomain.CurrentDomain.SetData("YAVSC_CONNECTION", ConnectionString);
|
||||
}
|
||||
|
||||
public void ConfigureServices (IServiceCollection services)
|
||||
{
|
||||
services.AddOptions();
|
||||
var siteSettingsconf = Configuration.GetSection("Site");
|
||||
services.Configure<SiteSettings>(siteSettingsconf);
|
||||
var smtpSettingsconf = Configuration.GetSection("Smtp");
|
||||
services.Configure<SmtpSettings>(smtpSettingsconf);
|
||||
services.AddInstance(typeof(ILoggerFactory), new LoggerFactory());
|
||||
services.AddTransient(typeof(IEmailSender), typeof(MailSender));
|
||||
services.AddTransient(typeof(RazorEngineHost), typeof(YaRazorEngineHost));
|
||||
services.AddEntityFramework().AddNpgsql().AddDbContext<ApplicationDbContext>();
|
||||
services.AddTransient((s) => new RazorTemplateEngine(s.GetService<RazorEngineHost>()));
|
||||
services.AddLogging();
|
||||
services.AddTransient<EMailer>();
|
||||
services.AddLocalization(options =>
|
||||
{
|
||||
options.ResourcesPath = "Resources";
|
||||
});
|
||||
|
||||
services.AddEntityFramework()
|
||||
.AddNpgsql()
|
||||
.AddDbContext<ApplicationDbContext>(
|
||||
db => db.UseNpgsql(ConnectionString)
|
||||
);
|
||||
services.Configure<SharedAuthenticationOptions>(options =>
|
||||
{
|
||||
options.SignInScheme = "Bearer";
|
||||
});
|
||||
|
||||
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>();
|
||||
|
||||
services.AddAuthentication();
|
||||
|
||||
}
|
||||
|
||||
public void Configure (IApplicationBuilder app, IHostingEnvironment env,
|
||||
IOptions<SiteSettings> siteSettings, ILoggerFactory loggerFactory)
|
||||
{
|
||||
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
|
||||
loggerFactory.AddDebug();
|
||||
logger = loggerFactory.CreateLogger<Startup>();
|
||||
logger.LogInformation(env.EnvironmentName);
|
||||
var authConf = Configuration.GetSection("Authentication").GetSection("Yavsc");
|
||||
var clientId = authConf.GetSection("ClientId").Value;
|
||||
var clientSecret = authConf.GetSection("ClientSecret").Value;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
24
cli/src/YavscServerFactory.cs
Normal file
24
cli/src/YavscServerFactory.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Hosting.Server;
|
||||
using Microsoft.AspNet.Http.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Yavsc.Models;
|
||||
|
||||
namespace Yavsc.Server
|
||||
{
|
||||
public class cliServerFactory : IServerFactory
|
||||
{
|
||||
public IFeatureCollection Initialize(IConfiguration configuration)
|
||||
{
|
||||
FeatureCollection featureCollection = new FeatureCollection();
|
||||
return featureCollection;
|
||||
}
|
||||
|
||||
public IDisposable Start(IFeatureCollection serverFeatures, Func<IFeatureCollection, Task> application)
|
||||
{
|
||||
var task = application(serverFeatures);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue