email refacts trying to use creds
+ Admin user deletion from user list + Admin send email confirmation message from user list + WebSocket minor change
This commit is contained in:
4
Makefile
4
Makefile
@ -6,6 +6,8 @@ test:
|
||||
web:
|
||||
make -C scripts/build/make watch
|
||||
|
||||
push:
|
||||
pushInProd:
|
||||
make -C src/Yavsc pushInProd
|
||||
|
||||
pushInPre:
|
||||
make -C src/Yavsc pushInPre
|
||||
|
@ -5,6 +5,8 @@ namespace Yavsc
|
||||
{
|
||||
public string Host { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public bool EnableSSL { get; set; }
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ namespace Yavsc.ViewModels.Account
|
||||
{
|
||||
public class UnregisterViewModel
|
||||
{
|
||||
public string UserId { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
}
|
||||
|
@ -30,41 +30,43 @@ public class GCMController : Controller
|
||||
public IActionResult Register(
|
||||
[FromBody] GoogleCloudMobileDeclaration declaration)
|
||||
{
|
||||
var uid = User.GetUserId();
|
||||
|
||||
_logger.LogInformation($"Registering device with id:{declaration.DeviceId} for {uid}");
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var alreadyRegisteredDevice = _context.GCMDevices.FirstOrDefault(d => d.DeviceId == declaration.DeviceId);
|
||||
var deviceAlreadyRegistered = (alreadyRegisteredDevice!=null);
|
||||
if (deviceAlreadyRegistered)
|
||||
{
|
||||
_logger.LogInformation($"deviceAlreadyRegistered");
|
||||
// Override an exiting owner
|
||||
alreadyRegisteredDevice.DeclarationDate = DateTime.Now;
|
||||
alreadyRegisteredDevice.DeviceOwnerId = uid;
|
||||
alreadyRegisteredDevice.GCMRegistrationId = declaration.GCMRegistrationId;
|
||||
alreadyRegisteredDevice.Model = declaration.Model;
|
||||
alreadyRegisteredDevice.Platform = declaration.Platform;
|
||||
alreadyRegisteredDevice.Version = declaration.Version;
|
||||
_context.Update(alreadyRegisteredDevice);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"new device");
|
||||
declaration.DeclarationDate = DateTime.Now;
|
||||
declaration.DeviceOwnerId = uid;
|
||||
_context.GCMDevices.Add(declaration as GoogleCloudMobileDeclaration);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
var latestActivityUpdate = _context.Activities.Max(a=>a.DateModified);
|
||||
return Json(new {
|
||||
IsAnUpdate = deviceAlreadyRegistered,
|
||||
UpdateActivities = (latestActivityUpdate != declaration.LatestActivityUpdate)
|
||||
});
|
||||
}
|
||||
var uid = User.GetUserId();
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
_logger.LogError("Invalid model for GCMD");
|
||||
return new BadRequestObjectResult(ModelState);
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Registering device with id:{declaration.DeviceId} for {uid}");
|
||||
var alreadyRegisteredDevice = _context.GCMDevices.FirstOrDefault(d => d.DeviceId == declaration.DeviceId);
|
||||
var deviceAlreadyRegistered = (alreadyRegisteredDevice!=null);
|
||||
if (deviceAlreadyRegistered)
|
||||
{
|
||||
_logger.LogInformation($"deviceAlreadyRegistered");
|
||||
// Override an exiting owner
|
||||
alreadyRegisteredDevice.DeclarationDate = DateTime.Now;
|
||||
alreadyRegisteredDevice.DeviceOwnerId = uid;
|
||||
alreadyRegisteredDevice.GCMRegistrationId = declaration.GCMRegistrationId;
|
||||
alreadyRegisteredDevice.Model = declaration.Model;
|
||||
alreadyRegisteredDevice.Platform = declaration.Platform;
|
||||
alreadyRegisteredDevice.Version = declaration.Version;
|
||||
_context.Update(alreadyRegisteredDevice);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation($"new device");
|
||||
declaration.DeclarationDate = DateTime.Now;
|
||||
declaration.DeviceOwnerId = uid;
|
||||
_context.GCMDevices.Add(declaration as GoogleCloudMobileDeclaration);
|
||||
_context.SaveChanges(User.GetUserId());
|
||||
}
|
||||
var latestActivityUpdate = _context.Activities.Max(a=>a.DateModified);
|
||||
return Json(new {
|
||||
IsAnUpdate = deviceAlreadyRegistered,
|
||||
UpdateActivities = (latestActivityUpdate != declaration.LatestActivityUpdate)
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ namespace Yavsc.Controllers
|
||||
ViewBag.hasNext = await users.CountAsync() > (toShow.Count() + shown);
|
||||
ViewBag.nextpage = pageNum+1;
|
||||
ViewBag.pageLen = pageLen;
|
||||
// ApplicationUser user;
|
||||
// user.EmailConfirmed
|
||||
return View(toShow.ToArray());
|
||||
}
|
||||
string GeneratePageToken() {
|
||||
@ -257,7 +259,14 @@ namespace Yavsc.Controllers
|
||||
|
||||
return View("AccountCreated");
|
||||
}
|
||||
AddErrors(result);
|
||||
else {
|
||||
_logger.LogError("Error registering from a valid model.");
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
_logger.LogError($"{error.Code} {error.Description}");
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
@ -265,11 +274,19 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
|
||||
[Authorize, HttpPost, ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> SendEMailForConfirm()
|
||||
public async Task<IActionResult> SendConfirationEmail()
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(User.GetUserId());
|
||||
var model = await SendEMailForConfirmAsync(user);
|
||||
return View("ConfirmEmailSent",model);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[Authorize("AdministratorOnly")]
|
||||
public async Task<IActionResult> AdminSendConfirationEmail(string id)
|
||||
{
|
||||
var user = await _userManager.FindByIdAsync(id);
|
||||
var model = await SendEMailForConfirmAsync(user);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
private async Task<EmailSentViewModel> SendEMailForConfirmAsync(ApplicationUser user)
|
||||
@ -422,7 +439,15 @@ namespace Yavsc.Controllers
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var result = await _userManager.ConfirmEmailAsync(user, code);
|
||||
IdentityResult result=null;
|
||||
try {
|
||||
result = await _userManager.ConfirmEmailAsync(user, code);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex.StackTrace);
|
||||
_logger.LogError(ex.Message);
|
||||
}
|
||||
return View(result.Succeeded ? "ConfirmEmail" : "Error");
|
||||
}
|
||||
|
||||
@ -645,7 +670,7 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("", "Code invalide ");
|
||||
ModelState.AddModelError("Code", "Code invalide ");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
@ -656,6 +681,12 @@ namespace Yavsc.Controllers
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet, Authorize("AdministratorOnly")]
|
||||
public IActionResult AdminDelete(string id)
|
||||
{
|
||||
return View(new UnregisterViewModel { UserId = id });
|
||||
}
|
||||
|
||||
[HttpPost, Authorize]
|
||||
public async Task<IActionResult> Delete(UnregisterViewModel model)
|
||||
{
|
||||
@ -663,8 +694,8 @@ namespace Yavsc.Controllers
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await _userManager.FindByIdAsync(User.GetUserId());
|
||||
var result = await _userManager.DeleteAsync(user);
|
||||
var result = await DeleteUser(model.UserId);
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
AddErrors(result);
|
||||
@ -674,6 +705,28 @@ namespace Yavsc.Controllers
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
async Task<IdentityResult> DeleteUser(string userId)
|
||||
{
|
||||
ApplicationUser user = await _userManager.FindByIdAsync(userId);
|
||||
_dbContext.GCMDevices.RemoveRange( _dbContext.GCMDevices.Where(g => g.DeviceOwnerId == userId ));
|
||||
// TODO add an owner to maillings
|
||||
_dbContext.MailingTemplate.RemoveRange(_dbContext.MailingTemplate.Where( t=>t.ManagerId == userId ));
|
||||
|
||||
return await _userManager.DeleteAsync(user);
|
||||
}
|
||||
|
||||
[HttpPost, Authorize("AdministratorOnly")]
|
||||
public async Task<IActionResult> AdminDelete(UnregisterViewModel model)
|
||||
{
|
||||
var result = await DeleteUser(model.UserId);
|
||||
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
AddErrors(result);
|
||||
return new BadRequestObjectResult(ModelState);
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
@ -687,7 +740,11 @@ namespace Yavsc.Controllers
|
||||
|
||||
private async Task<ApplicationUser> GetCurrentUserAsync()
|
||||
{
|
||||
return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
|
||||
return await GetCurrentUserAsync(HttpContext.User.GetUserId());
|
||||
}
|
||||
private async Task<ApplicationUser> GetCurrentUserAsync(string id)
|
||||
{
|
||||
return await _userManager.FindByIdAsync(id);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
@ -61,7 +62,13 @@ namespace Yavsc.Services
|
||||
sc.Connect(
|
||||
smtpSettings.Host,
|
||||
smtpSettings.Port,
|
||||
SecureSocketOptions.None);
|
||||
SecureSocketOptions.Auto);
|
||||
if (smtpSettings.UserName!=null) {
|
||||
NetworkCredential creds = new NetworkCredential(
|
||||
smtpSettings.UserName, smtpSettings.Password, smtpSettings.Host);
|
||||
await sc.AuthenticateAsync(System.Text.Encoding.UTF8, creds, System.Threading.CancellationToken.None);
|
||||
}
|
||||
|
||||
await sc.SendAsync(msg);
|
||||
model.MessageId = msg.MessageId;
|
||||
model.Sent = true; // a duplicate info to remove from the view model, that equals to MessageId == null
|
||||
|
@ -12,7 +12,7 @@ namespace Yavsc
|
||||
public void ConfigureProtectionServices(IServiceCollection services)
|
||||
{
|
||||
|
||||
services.AddDataProtection();
|
||||
services.AddDataProtection();
|
||||
services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator),
|
||||
typeof(SystemWebApplicationDiscriminator)));
|
||||
|
||||
@ -23,7 +23,6 @@ namespace Yavsc
|
||||
configure.PersistKeysToFileSystem(
|
||||
new DirectoryInfo(Configuration["DataProtection:Keys:Dir"]));
|
||||
});
|
||||
|
||||
}
|
||||
private sealed class SystemWebApplicationDiscriminator : IApplicationDiscriminator
|
||||
{
|
||||
|
@ -124,7 +124,6 @@ namespace Yavsc {
|
||||
AccessType = "offline",
|
||||
Scope = {
|
||||
"profile",
|
||||
"https://www.googleapis.com/auth/plus.login",
|
||||
"https://www.googleapis.com/auth/admin.directory.resource.calendar",
|
||||
"https://www.googleapis.com/auth/calendar",
|
||||
"https://www.googleapis.com/auth/calendar.events"
|
||||
|
@ -22,9 +22,6 @@ namespace Yavsc
|
||||
};
|
||||
app.UseWebSockets(webSocketOptions);
|
||||
app.UseSignalR(Constants.SignalRPath);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task Echo(HttpContext context, WebSocket webSocket)
|
||||
|
@ -439,12 +439,7 @@ namespace Yavsc
|
||||
|
||||
var uname = context.User.GetUserName();
|
||||
// ensure uniqueness of casting stream from this user
|
||||
if (LiveApiController.Casters.ContainsKey(uname))
|
||||
{
|
||||
logger.LogWarning("already casting: "+uname);
|
||||
context.Response.StatusCode = 400;
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
var uid = context.User.GetUserId();
|
||||
// get some setup from user
|
||||
@ -454,10 +449,30 @@ namespace Yavsc
|
||||
context.Response.StatusCode = 400;
|
||||
}
|
||||
else {
|
||||
// Accept the socket
|
||||
var meta = new LiveCastMeta { Socket = await context.WebSockets.AcceptWebSocketAsync() };
|
||||
LiveCastMeta meta=null;
|
||||
if (LiveApiController.Casters.ContainsKey(uname))
|
||||
{
|
||||
meta = LiveApiController.Casters[uname];
|
||||
if (meta.Socket.State != WebSocketState.Closed) {
|
||||
// FIXME loosed connexion should be detected & disposed else where
|
||||
meta.Socket.Dispose();
|
||||
meta.Socket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
} else {
|
||||
meta.Socket.Dispose();
|
||||
// Accept the socket
|
||||
meta.Socket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Accept the socket
|
||||
meta = new LiveCastMeta { Socket = await context.WebSockets.AcceptWebSocketAsync() };
|
||||
}
|
||||
logger.LogInformation("Accepted web socket");
|
||||
// Dispatch the flow
|
||||
try {
|
||||
|
||||
|
||||
|
||||
if (meta.Socket != null && meta.Socket.State == WebSocketState.Open)
|
||||
{
|
||||
@ -466,7 +481,7 @@ namespace Yavsc
|
||||
// Find receivers: others in the chat room
|
||||
// send them the flow
|
||||
|
||||
var sBuffer = System.Net.WebSockets.WebSocket.CreateServerBuffer(1024);
|
||||
var sBuffer = new ArraySegment<byte>(new byte[1024]);
|
||||
logger.LogInformation("Receiving bytes...");
|
||||
|
||||
WebSocketReceiveResult received = await meta.Socket.ReceiveAsync(sBuffer, CancellationToken.None);
|
||||
@ -479,41 +494,70 @@ namespace Yavsc
|
||||
|
||||
// FIXME do we really need to close those one in invalid state ?
|
||||
Stack<string> ToClose = new Stack<string>();
|
||||
|
||||
while (received.MessageType != WebSocketMessageType.Close)
|
||||
{
|
||||
logger.LogInformation($"Echoing {received.Count} bytes received in a {received.MessageType} message; Fin={received.EndOfMessage}");
|
||||
// Echo anything we receive
|
||||
// and send to all listner found
|
||||
foreach (var cliItem in meta.Listeners)
|
||||
|
||||
try {
|
||||
while (received.MessageType != WebSocketMessageType.Close)
|
||||
{
|
||||
var listenningSocket = cliItem.Value;
|
||||
if (listenningSocket.State == WebSocketState.Open)
|
||||
await listenningSocket.SendAsync(
|
||||
sBuffer, received.MessageType, received.EndOfMessage, CancellationToken.None);
|
||||
else ToClose.Push(cliItem.Key);
|
||||
logger.LogInformation($"Echoing {received.Count} bytes received in a {received.MessageType} message; Fin={received.EndOfMessage}");
|
||||
// Echo anything we receive
|
||||
// and send to all listner found
|
||||
foreach (var cliItem in meta.Listeners)
|
||||
{
|
||||
var listenningSocket = cliItem.Value;
|
||||
if (listenningSocket.State == WebSocketState.Open)
|
||||
await listenningSocket.SendAsync(
|
||||
sBuffer, received.MessageType, received.EndOfMessage, CancellationToken.None);
|
||||
else
|
||||
if (listenningSocket.State == WebSocketState.CloseReceived || listenningSocket.State == WebSocketState.CloseSent)
|
||||
{
|
||||
ToClose.Push(cliItem.Key);
|
||||
}
|
||||
}
|
||||
received = await meta.Socket.ReceiveAsync(sBuffer, CancellationToken.None);
|
||||
|
||||
string no;
|
||||
do
|
||||
{
|
||||
no = ToClose.Pop();
|
||||
WebSocket listenningSocket;
|
||||
if (meta.Listeners.TryRemove(no, out listenningSocket))
|
||||
await listenningSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, "State != WebSocketState.Open", CancellationToken.None);
|
||||
|
||||
} while (no != null);
|
||||
}
|
||||
received = await meta.Socket.ReceiveAsync(sBuffer, CancellationToken.None);
|
||||
|
||||
string no;
|
||||
do
|
||||
{
|
||||
no = ToClose.Pop();
|
||||
WebSocket listenningSocket;
|
||||
if (meta.Listeners.TryRemove(no, out listenningSocket))
|
||||
await listenningSocket.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, "State != WebSocketState.Open", CancellationToken.None);
|
||||
|
||||
} while (no != null);
|
||||
await meta.Socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "eof", CancellationToken.None);
|
||||
LiveApiController.Casters[uname] = null;
|
||||
} catch (Exception ex)
|
||||
{
|
||||
logger.LogError($"Exception occured : {ex.Message}");
|
||||
logger.LogError(ex.StackTrace);
|
||||
meta.Socket.Dispose();
|
||||
LiveApiController.Casters[uname] = null;
|
||||
}
|
||||
await meta.Socket.CloseAsync(received.CloseStatus.Value, received.CloseStatusDescription, CancellationToken.None);
|
||||
LiveApiController.Casters[uname] = null;
|
||||
}
|
||||
else { // not meta.Socket != null && meta.Socket.State == WebSocketState.Open
|
||||
if (meta.Socket != null)
|
||||
logger.LogError($"meta.Socket.State: {meta.Socket.State.ToString()} ");
|
||||
else logger.LogError("socket object is null");
|
||||
if (meta.Socket != null) {
|
||||
logger.LogError($"meta.Socket.State not Open: {meta.Socket.State.ToString()} ");
|
||||
meta.Socket.Dispose();
|
||||
}
|
||||
else
|
||||
logger.LogError("socket object is null");
|
||||
}
|
||||
}}}}}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
if (ex.Message == "Unexpected end of stream")
|
||||
{
|
||||
logger.LogError($"Unexpected end of stream");
|
||||
}
|
||||
else {
|
||||
logger.LogError($"Really unexpected end of stream");
|
||||
}
|
||||
await meta.Socket?.CloseAsync(WebSocketCloseStatus.EndpointUnavailable, ex.Message, CancellationToken.None);
|
||||
meta.Socket?.Dispose();
|
||||
LiveApiController.Casters[uname] = null;
|
||||
}
|
||||
}}}}
|
||||
else
|
||||
{
|
||||
await next();
|
||||
|
@ -5,4 +5,6 @@
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
Your account has successfully been created.
|
||||
|
||||
<a asp-action="Index" asp-controller="Home">Return to home</a>
|
||||
|
14
src/Yavsc/Views/Account/AdminDelete.cshtml
Normal file
14
src/Yavsc/Views/Account/AdminDelete.cshtml
Normal file
@ -0,0 +1,14 @@
|
||||
@model UnregisterViewModel
|
||||
@{
|
||||
ViewData["Title"] = @SR["Unregister"];
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"].</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="AdminDelete" method="post" class="form-horizontal" role="form">
|
||||
|
||||
@Html.Hidden("UserId")
|
||||
@Html.Hidden("ReturnUrl")
|
||||
<input type="submit" value="@SR["Unregister"]" class="btn btn-default"/>
|
||||
</form>
|
||||
|
12
src/Yavsc/Views/Account/AdminSendConfirationEmail.cshtml
Executable file
12
src/Yavsc/Views/Account/AdminSendConfirationEmail.cshtml
Executable file
@ -0,0 +1,12 @@
|
||||
@model Yavsc.Abstract.Manage.EmailSentViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "S'il vous plait, veuillez confirmer votre adresse e-mail";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"].</h2>
|
||||
<div>
|
||||
<p>
|
||||
Un message vient d' être envoyé à l'adresse e-mail ( @Model.EMail , id:@Model.MessageId ).
|
||||
</p>
|
||||
</div>
|
@ -51,6 +51,10 @@
|
||||
</dt>
|
||||
<dd>
|
||||
@Html.DisplayFor(m=>user.Email)
|
||||
@if (!user.EmailConfirmed) {
|
||||
<a asp-action="AdminSendConfirationEmail" asp-route-id="@user.Id" >Envoyer une demande de confirmation</a>
|
||||
}
|
||||
<a asp-action="AdminDelete" asp-route-id="@user.Id" >Supprimer</a>
|
||||
</dd>
|
||||
</dl>
|
||||
</td>
|
||||
|
@ -46,4 +46,14 @@ Nombre </dt><dd> @Model.AdminCount</dd>
|
||||
|
||||
<a asp-controller="HairTaints" class="btn btn-primary">
|
||||
Gestion des couleurs
|
||||
</a>
|
||||
</a>
|
||||
|
||||
<h3>GCM Devices</h3>
|
||||
<a asp-controller="GCMDevices" class="btn btn-primary">
|
||||
Google Cloud Messaging
|
||||
</a>
|
||||
|
||||
<h3>Applications tièrces</h3>
|
||||
<a asp-controller="Client" class="btn btn-primary">
|
||||
@SR["OAuth key management"]
|
||||
</a>
|
||||
|
@ -26,7 +26,7 @@
|
||||
} else {
|
||||
<text>
|
||||
<i> (@SR["Adresse non confirmée."])</i>
|
||||
<form asp-action="SendEMailForConfirm" asp-controller="Account" enctype="multipart/form-data">
|
||||
<form asp-action="SendConfirationEmail" asp-controller="Account" enctype="multipart/form-data">
|
||||
<input type="submit" value="@SR["Confirmer cette adresse"]"/>
|
||||
</form>
|
||||
</text>
|
||||
|
Reference in New Issue
Block a user