[WIP] chat hub.
This commit is contained in:
@ -23,6 +23,8 @@ namespace Yavsc.Abstract.Streaming
|
||||
// A name where to save this stream, relative to user's files root
|
||||
string DifferedFileName { get; set; }
|
||||
|
||||
int SequenceNumber { get; set; }
|
||||
|
||||
[Required]
|
||||
string OwnerId {get; set; }
|
||||
|
||||
|
@ -11,6 +11,9 @@ namespace Yavsc
|
||||
TokenPath = "~/token",
|
||||
LoginPath = "~/signin",
|
||||
LogoutPath = "~/signout", UserInfoPath = "~/api/me",
|
||||
|
||||
SignalRPath = "/api/signalr",
|
||||
|
||||
ApplicationAuthenticationSheme = "ServerCookie",
|
||||
ExternalAuthenticationSheme= "ExternalCookie",
|
||||
CompanyInfoUrl = " https://societeinfo.com/app/rest/api/v1/company/json?registration_number={0}&key={1}",
|
||||
|
@ -13,17 +13,26 @@ namespace Yavsc.Models.Streaming
|
||||
// set by the server, unique
|
||||
public long Id { get; set; }
|
||||
|
||||
// a title for this flow
|
||||
//
|
||||
/// <summary>
|
||||
/// a title for this flow
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[StringLength(255)]
|
||||
public string Title { get; set; }
|
||||
|
||||
// a little description
|
||||
[StringLength(1023)]
|
||||
public string Pitch { get; set; }
|
||||
|
||||
// The stream type
|
||||
[StringLength(127)]
|
||||
public string MediaType { get; set; }
|
||||
|
||||
// A name where to save this stream, relative to user's files root
|
||||
[StringLength(255)]
|
||||
public string DifferedFileName { get; set; }
|
||||
public int SequenceNumber { get; set; }
|
||||
|
||||
[Required]
|
||||
public string OwnerId {get; set; }
|
||||
|
@ -8,15 +8,17 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.AspNet.SignalR;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Yavsc.Helpers;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.Streaming;
|
||||
using Yavsc.ViewModels.Streaming;
|
||||
|
||||
namespace Yavsc.Controllers
|
||||
{
|
||||
[Route("api/LiveApi")]
|
||||
[Route("api/live")]
|
||||
public class LiveApiController : Controller
|
||||
{
|
||||
public static ConcurrentDictionary<string, LiveCastMeta> Casters = new ConcurrentDictionary<string, LiveCastMeta>();
|
||||
@ -37,7 +39,10 @@ namespace Yavsc.Controllers
|
||||
_dbContext = context;
|
||||
_logger = loggerFactory.CreateLogger<LiveApiController>();
|
||||
}
|
||||
|
||||
public async Task<string[]> GetFileNameHint(string id)
|
||||
{
|
||||
return await _dbContext.Tags.Where( t=> t.Name.StartsWith(id)).Select(t=>t.Name).Take(25).ToArrayAsync();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetLive(string id)
|
||||
{
|
||||
@ -69,7 +74,7 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
var uid = User.GetUserId();
|
||||
// get some setup from user
|
||||
var flow = _dbContext.LiveFlow.SingleOrDefault(f=> (f.OwnerId==uid && f.Id == id));
|
||||
var flow = _dbContext.LiveFlow.Include(f=>f.Owner).SingleOrDefault(f=> (f.OwnerId==uid && f.Id == id));
|
||||
if (flow == null)
|
||||
{
|
||||
ModelState.AddModelError("error",$"You don't own any flow with the id {id}");
|
||||
@ -92,6 +97,11 @@ namespace Yavsc.Controllers
|
||||
WebSocketReceiveResult received = await meta.Socket.ReceiveAsync
|
||||
(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
|
||||
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
|
||||
|
||||
hubContext.Clients.All.addPublicStream(new { id = flow.Id, sender = flow.Owner.UserName, title = flow.Title, url = flow.GetFileUrl(),
|
||||
mediaType = flow.MediaType }, $"{flow.Owner.UserName} is starting a stream!");
|
||||
|
||||
// FIXME do we really need to close those one in invalid state ?
|
||||
Stack<string> ToClose = new Stack<string>();
|
||||
|
||||
@ -148,7 +158,6 @@ namespace Yavsc.Controllers
|
||||
}
|
||||
|
||||
|
||||
// GET: api/LiveApi/5
|
||||
[HttpGet("{id}", Name = "GetLiveFlow")]
|
||||
public async Task<IActionResult> GetLiveFlow([FromRoute] long id)
|
||||
{
|
||||
@ -167,7 +176,6 @@ namespace Yavsc.Controllers
|
||||
return Ok(liveFlow);
|
||||
}
|
||||
|
||||
// PUT: api/LiveApi/5
|
||||
[HttpPut("{id}")]
|
||||
public async Task<IActionResult> PutLiveFlow([FromRoute] long id, [FromBody] LiveFlow liveFlow)
|
||||
{
|
||||
@ -208,7 +216,6 @@ namespace Yavsc.Controllers
|
||||
return new HttpStatusCodeResult(StatusCodes.Status204NoContent);
|
||||
}
|
||||
|
||||
// POST: api/LiveApi
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> PostLiveFlow([FromBody] LiveFlow liveFlow)
|
||||
{
|
||||
|
@ -37,8 +37,9 @@ namespace Yavsc
|
||||
app.UseAppBuilder(appBuilder => appBuilder.MapSignalR(
|
||||
path,
|
||||
new HubConfiguration() {
|
||||
EnableDetailedErrors = false,
|
||||
EnableJSONP = false
|
||||
EnableDetailedErrors = true,
|
||||
EnableJSONP = true,
|
||||
EnableJavaScriptProxies = true
|
||||
}
|
||||
));
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ using Yavsc.Abstract.FileSystem;
|
||||
using Yavsc.Exceptions;
|
||||
using Yavsc.Models;
|
||||
using Yavsc.Models.FileSystem;
|
||||
using Yavsc.Models.Streaming;
|
||||
using Yavsc.ViewModels;
|
||||
|
||||
namespace Yavsc.Helpers
|
||||
@ -188,6 +189,16 @@ public static FileRecievedInfo ReceiveProSignature(this ClaimsPrincipal user, st
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
public static string GetFileUrl (this LiveFlow flow)
|
||||
{
|
||||
if (flow.DifferedFileName==null)
|
||||
// no server-side backup for this stream
|
||||
return null;
|
||||
var fileInfo = new FileInfo(flow.DifferedFileName);
|
||||
var ext = fileInfo.Extension;
|
||||
var namelen = flow.DifferedFileName.Length - ext.Length;
|
||||
var basename = flow.DifferedFileName.Substring(0,namelen);
|
||||
return $"{Startup.UserFilesOptions.RequestPath}/{flow.Owner.UserName}/live/{basename}-{flow.SequenceNumber}{ext}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,16 @@ using Microsoft.AspNet.SignalR;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNet.Authorization;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Models;
|
||||
using Models.Chat;
|
||||
using Yavsc.ViewModels.Auth;
|
||||
|
||||
public class ChatHub : Hub, IDisposable
|
||||
{
|
||||
@ -49,7 +46,7 @@ namespace Yavsc
|
||||
_logger = loggerFactory.CreateLogger<ChatHub>();
|
||||
}
|
||||
|
||||
public override Task OnConnected()
|
||||
public override async Task OnConnected()
|
||||
{
|
||||
bool isAuth = false;
|
||||
string userName = null;
|
||||
@ -60,27 +57,35 @@ namespace Yavsc
|
||||
var group = isAuth ?
|
||||
"authenticated" : "anonymous";
|
||||
// Log ("Cx: " + group);
|
||||
Groups.Add(Context.ConnectionId, group);
|
||||
await Groups.Add(Context.ConnectionId, group);
|
||||
if (isAuth)
|
||||
{
|
||||
|
||||
var user = _dbContext.Users.Single(u => u.UserName == userName);
|
||||
/*
|
||||
var user = _dbContext.Users.Include(u=>u.Connections).Single(u => u.UserName == userName);
|
||||
if (user.Connections==null)
|
||||
user.Connections = new List<ChatConnection>();
|
||||
var ucx = user.Connections.FirstOrDefault(c=>c.ConnectionId == Context.ConnectionId);
|
||||
if (ucx==null)
|
||||
user.Connections.Add(new ChatConnection
|
||||
{
|
||||
ConnectionId = Context.ConnectionId,
|
||||
UserAgent = Context.Request.Headers["User-Agent"],
|
||||
Connected = true
|
||||
});
|
||||
else {
|
||||
ucx.Connected = true;
|
||||
}
|
||||
_dbContext.SaveChanges();
|
||||
|
||||
Clients.CallerState.BlackListedBy = await _dbContext.BlackListed.Where(r=>r.UserId == user.Id).Select(r=>r.OwnerId).ToArrayAsync();
|
||||
*/
|
||||
}
|
||||
}
|
||||
else Groups.Add(Context.ConnectionId, "anonymous");
|
||||
else await Groups.Add(Context.ConnectionId, "anonymous");
|
||||
|
||||
Clients.Group("authenticated").notify("connected", Context.ConnectionId, userName);
|
||||
|
||||
return base.OnConnected();
|
||||
await OnConnected();
|
||||
}
|
||||
|
||||
public override Task OnDisconnected(bool stopCalled)
|
||||
@ -140,7 +145,7 @@ namespace Yavsc
|
||||
{
|
||||
string uname = (Context.User != null) ?
|
||||
$"[{Context.User.Identity.Name}]" :
|
||||
$"({name})";
|
||||
$"?{name}";
|
||||
Clients.All.addMessage(uname, message);
|
||||
}
|
||||
|
||||
@ -149,22 +154,18 @@ namespace Yavsc
|
||||
[Authorize]
|
||||
public async void SendPV(string connectionId, string message)
|
||||
{
|
||||
// TODO personal black|white list +
|
||||
// Contact list allowed only +
|
||||
// only pro
|
||||
string destUserId = (await _dbContext.ChatConnection.SingleAsync (c=>c.ConnectionId == connectionId)).ApplicationUserId;
|
||||
|
||||
var sender = Context.User.Identity.Name;
|
||||
var allow = await AllowPv(connectionId);
|
||||
if (!allow) {
|
||||
Clients.Caller.addPV(sender, "[private message was refused]");
|
||||
if (Clients.CallerState.BlackListedBy!=null)
|
||||
foreach (string destId in Clients.CallerState.BlackListedBy)
|
||||
{
|
||||
if (_dbContext.ChatConnection.Any(c => c.ConnectionId == connectionId && c.ApplicationUserId == destId ))
|
||||
{
|
||||
_logger.LogInformation($"PV aborted by black list");
|
||||
Clients.Caller.send("denied");
|
||||
return ;
|
||||
}
|
||||
|
||||
var hubCxContext = Clients.User(connectionId);
|
||||
|
||||
}
|
||||
var cli = Clients.Client(connectionId);
|
||||
cli.addPV(sender, message);
|
||||
cli.addPV(Context.User.Identity.Name, message);
|
||||
}
|
||||
|
||||
private async Task<bool> AllowPv(string destConnectionId)
|
||||
|
@ -1,33 +1,31 @@
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using Google.Apis.Auth.OAuth2.Responses;
|
||||
using Google.Apis.Util.Store;
|
||||
using Microsoft.AspNet.Authentication;
|
||||
using Microsoft.AspNet.Authentication.Cookies;
|
||||
using Microsoft.AspNet.Authentication.Facebook;
|
||||
using Microsoft.AspNet.Authentication.Twitter;
|
||||
using Microsoft.AspNet.Authentication.JwtBearer;
|
||||
using Microsoft.AspNet.Authentication.OAuth;
|
||||
using Microsoft.AspNet.Authentication.Twitter;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.EntityFramework;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.OptionsModel;
|
||||
using Microsoft.Extensions.WebEncoders;
|
||||
using OAuth.AspNet.AuthServer;
|
||||
using OAuth.AspNet.Tokens;
|
||||
using Google.Apis.Util.Store;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Google.Apis.Auth.OAuth2.Responses;
|
||||
|
||||
namespace Yavsc
|
||||
{
|
||||
namespace Yavsc {
|
||||
using Auth;
|
||||
using Extensions;
|
||||
using Models;
|
||||
using Helpers.Google;
|
||||
using Models;
|
||||
|
||||
public partial class Startup
|
||||
{
|
||||
public partial class Startup {
|
||||
public static CookieAuthenticationOptions ExternalCookieAppOptions { get; private set; }
|
||||
|
||||
public static IdentityOptions IdentityAppOptions { get; set; }
|
||||
@ -36,7 +34,6 @@ namespace Yavsc
|
||||
public static TwitterOptions TwitterAppOptions { get; private set; }
|
||||
public static OAuthAuthorizationServerOptions OAuthServerAppOptions { get; private set; }
|
||||
|
||||
|
||||
public static YavscGoogleOptions YavscGoogleAppOptions { get; private set; }
|
||||
public static MonoDataProtectionProvider ProtectionProvider { get; private set; }
|
||||
|
||||
@ -50,8 +47,7 @@ namespace Yavsc
|
||||
// used by the YavscGoogleOAuth middelware (TODO drop it)
|
||||
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder> ();
|
||||
|
||||
services.AddAuthentication(options =>
|
||||
{
|
||||
services.AddAuthentication (options => {
|
||||
options.SignInScheme = Constants.ExternalAuthenticationSheme;
|
||||
});
|
||||
|
||||
@ -60,8 +56,7 @@ namespace Yavsc
|
||||
(ProtectionProvider);
|
||||
|
||||
services.AddIdentity<ApplicationUser, IdentityRole> (
|
||||
option =>
|
||||
{
|
||||
option => {
|
||||
IdentityAppOptions = option;
|
||||
option.User.AllowedUserNameCharacters += " ";
|
||||
option.User.RequireUniqueEmail = true;
|
||||
@ -93,16 +88,13 @@ namespace Yavsc
|
||||
;
|
||||
}
|
||||
private void ConfigureOAuthApp (IApplicationBuilder app,
|
||||
SiteSettings settingsOptions, ILogger logger)
|
||||
{
|
||||
SiteSettings settingsOptions, ILogger logger) {
|
||||
|
||||
app.UseIdentity ();
|
||||
app.UseWhen (context => context.Request.Path.StartsWithSegments ("/api"),
|
||||
branch =>
|
||||
{
|
||||
branch => {
|
||||
branch.UseJwtBearerAuthentication (
|
||||
options =>
|
||||
{
|
||||
options => {
|
||||
options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.AutomaticAuthenticate = true;
|
||||
options.SecurityTokenValidators.Clear ();
|
||||
@ -112,13 +104,8 @@ namespace Yavsc
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"),
|
||||
branch =>
|
||||
{
|
||||
// External authentication shared cookie:
|
||||
branch.UseCookieAuthentication(options =>
|
||||
{
|
||||
branch.UseCookieAuthentication (options => {
|
||||
ExternalCookieAppOptions = options;
|
||||
options.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
|
||||
options.AutomaticAuthenticate = true;
|
||||
@ -128,26 +115,23 @@ namespace Yavsc
|
||||
options.AccessDeniedPath = new PathString (Constants.LoginPath.Substring (1));
|
||||
});
|
||||
|
||||
|
||||
|
||||
YavscGoogleAppOptions = new YavscGoogleOptions
|
||||
{
|
||||
YavscGoogleAppOptions = new YavscGoogleOptions {
|
||||
ClientId = GoogleWebClientConfiguration["web:client_id"],
|
||||
ClientSecret = GoogleWebClientConfiguration["web:client_secret"],
|
||||
AccessType = "offline",
|
||||
Scope = { "profile", "https://www.googleapis.com/auth/plus.login",
|
||||
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"},
|
||||
"https://www.googleapis.com/auth/calendar.events"
|
||||
},
|
||||
SaveTokensAsClaims = true,
|
||||
UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me",
|
||||
Events = new OAuthEvents
|
||||
{
|
||||
OnCreatingTicket = async context =>
|
||||
{
|
||||
Events = new OAuthEvents {
|
||||
OnCreatingTicket = async context => {
|
||||
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory> ()
|
||||
.CreateScope())
|
||||
{
|
||||
.CreateScope ()) {
|
||||
var gcontext = context as GoogleOAuthCreatingTicketContext;
|
||||
context.Identity.AddClaim (new Claim (YavscClaimTypes.GoogleUserId, gcontext.GoogleUserId));
|
||||
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext> ();
|
||||
@ -182,11 +166,9 @@ namespace Yavsc
|
||||
options.ConsumerSecret = Configuration["Authentication:Twitter:ClientSecret"];
|
||||
}); */
|
||||
|
||||
|
||||
branch.UseOAuthAuthorizationServer (
|
||||
|
||||
options =>
|
||||
{
|
||||
options => {
|
||||
OAuthServerAppOptions = options;
|
||||
options.AuthorizeEndpointPath = new PathString (Constants.AuthorizePath.Substring (1));
|
||||
options.TokenEndpointPath = new PathString (Constants.TokenPath.Substring (1));
|
||||
@ -195,22 +177,19 @@ namespace Yavsc
|
||||
options.AuthenticationScheme = OAuthDefaults.AuthenticationType;
|
||||
options.TokenDataProtector = ProtectionProvider.CreateProtector ("Bearer protection");
|
||||
|
||||
options.Provider = new OAuthAuthorizationServerProvider
|
||||
{
|
||||
options.Provider = new OAuthAuthorizationServerProvider {
|
||||
OnValidateClientRedirectUri = ValidateClientRedirectUri,
|
||||
OnValidateClientAuthentication = ValidateClientAuthentication,
|
||||
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
|
||||
OnGrantClientCredentials = GrantClientCredetails
|
||||
};
|
||||
|
||||
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
|
||||
{
|
||||
options.AuthorizationCodeProvider = new AuthenticationTokenProvider {
|
||||
OnCreate = CreateAuthenticationCode,
|
||||
OnReceive = ReceiveAuthenticationCode,
|
||||
};
|
||||
|
||||
options.RefreshTokenProvider = new AuthenticationTokenProvider
|
||||
{
|
||||
options.RefreshTokenProvider = new AuthenticationTokenProvider {
|
||||
OnCreate = CreateRefreshToken,
|
||||
OnReceive = ReceiveRefreshToken,
|
||||
};
|
||||
@ -223,7 +202,6 @@ namespace Yavsc
|
||||
|
||||
Environment.SetEnvironmentVariable ("GOOGLE_APPLICATION_CREDENTIALS", "google-secret.json");
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ namespace Yavsc
|
||||
SiteSettings siteSettings, IHostingEnvironment env)
|
||||
{
|
||||
app.UseWebSockets();
|
||||
app.UseSignalR("/api/signalr");
|
||||
app.UseSignalR(Constants.SignalRPath);
|
||||
}
|
||||
}
|
||||
}
|
@ -393,13 +393,14 @@ namespace Yavsc
|
||||
options.AuthenticationDescriptions.Clear();
|
||||
options.AutomaticAuthentication = false;
|
||||
});
|
||||
app.UseSession();
|
||||
|
||||
ConfigureOAuthApp(app, SiteSetup, logger);
|
||||
ConfigureFileServerApp(app, SiteSetup, env, authorizationService);
|
||||
ConfigureWebSocketsApp(app, SiteSetup, env);
|
||||
ConfigureWorkflow(app, SiteSetup, logger);
|
||||
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture) new RequestCulture((string)"en-US"));
|
||||
app.UseSession();
|
||||
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
|
@ -90,8 +90,8 @@
|
||||
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-*",
|
||||
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
|
||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-rc1-final",
|
||||
"Microsoft.AspNet.SignalR.Core": "2.3.0",
|
||||
"Microsoft.AspNet.SignalR.JS": "2.3.0",
|
||||
"Microsoft.AspNet.SignalR.Core": "2.2.1",
|
||||
"Microsoft.AspNet.SignalR.JS": "2.2.1",
|
||||
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-*",
|
||||
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-*",
|
||||
"Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-*",
|
||||
|
Reference in New Issue
Block a user