diff --git a/Yavsc/Controllers/IT/GitController.cs b/Yavsc/Controllers/IT/GitController.cs index 0af04581..41acb941 100644 --- a/Yavsc/Controllers/IT/GitController.cs +++ b/Yavsc/Controllers/IT/GitController.cs @@ -115,7 +115,7 @@ namespace Yavsc.Controllers { return HttpNotFound(); } - ViewData["OwnerId"] = new SelectList(_context.ApplicationUser, "Id", "Owner", gitRepositoryReference.OwnerId); + ViewBag.OwnerId = new SelectList(_context.ApplicationUser, "Id", "Owner", gitRepositoryReference.OwnerId); return View(gitRepositoryReference); } diff --git a/Yavsc/Controllers/ProjectController.cs b/Yavsc/Controllers/IT/ProjectController.cs similarity index 81% rename from Yavsc/Controllers/ProjectController.cs rename to Yavsc/Controllers/IT/ProjectController.cs index 332614c5..c045ea9b 100644 --- a/Yavsc/Controllers/ProjectController.cs +++ b/Yavsc/Controllers/IT/ProjectController.cs @@ -10,6 +10,8 @@ using Yavsc.Server.Helpers; using Yavsc.Models.Workflow; using Yavsc.Models.Payment; using Yavsc.Server.Models.IT.SourceCode; +using Microsoft.AspNet.Mvc.Localization; +using Microsoft.Extensions.Localization; namespace Yavsc.Controllers { @@ -18,11 +20,17 @@ namespace Yavsc.Controllers { private ApplicationDbContext _context; ILogger _logger; + IStringLocalizer _localizer; - public ProjectController(ApplicationDbContext context, ILoggerFactory loggerFactory) + public ProjectController(ApplicationDbContext context, + ILoggerFactory loggerFactory, + IStringLocalizer localizer + ) { _context = context; + _localizer = localizer; _logger = loggerFactory.CreateLogger(); + } // GET: Project @@ -105,11 +113,14 @@ namespace Yavsc.Controllers { return HttpNotFound(); } - ViewData["ClientId"] = new SelectList(_context.ApplicationUser, "Id", "Client", project.ClientId); - ViewData["ActivityCode"] = new SelectList(_context.Activities, "Code", "Context", project.ActivityCode); - ViewData["PerformerId"] = new SelectList(_context.Performers, "PerformerId", "PerformerProfile", project.PerformerId); - ViewData["PaymentId"] = new SelectList(_context.PayPalPayments, "CreationToken", "Regularisation", project.PaymentId); - ViewData["Name"] = new SelectList(_context.GitRepositoryReference, "Path", "Repository", project.Name); + /* ViewBag.ClientId = new SelectList(_context.ApplicationUser, "Id", "Client", project.ClientId); + ViewBag.ActivityCodeItems = new SelectList(_context.Activities, "Code", "Context", project.ActivityCode); + ViewBag.PerformerId = new SelectList(_context.Performers, "PerformerId", "PerformerProfile", project.PerformerId); + ViewBag.PaymentId = new SelectList(_context.PayPalPayments, "CreationToken", "Regularisation", project.PaymentId); + ViewBag.Name = new SelectList(_context.GitRepositoryReference, "Path", "Repository", project.Name); + */ + ViewBag.Status = Yavsc.Extensions.EnumExtensions.GetSelectList(typeof(QueryStatus), _localizer, project.Status); + ViewBag.Repository = new SelectList(_context.GitRepositoryReference, "Path", "Repository", project.Repository); return View(project); } @@ -124,11 +135,6 @@ namespace Yavsc.Controllers await _context.SaveChangesAsync(); return RedirectToAction("Index"); } - ViewData["ClientId"] = new SelectList(_context.ApplicationUser, "Id", "Client", project.ClientId); - ViewData["ActivityCode"] = new SelectList(_context.Activities, "Code", "Context", project.ActivityCode); - ViewData["PerformerId"] = new SelectList(_context.Performers, "PerformerId", "PerformerProfile", project.PerformerId); - ViewData["PaymentId"] = new SelectList(_context.PayPalPayments, "CreationToken", "Regularisation", project.PaymentId); - ViewData["Name"] = new SelectList(_context.GitRepositoryReference, "Path", "Repository", project.Name); return View(project); } diff --git a/Yavsc/Views/Git/Edit.cshtml b/Yavsc/Views/Git/Edit.cshtml index 178b7670..1b8f060d 100644 --- a/Yavsc/Views/Git/Edit.cshtml +++ b/Yavsc/Views/Git/Edit.cshtml @@ -17,7 +17,7 @@
-
diff --git a/Yavsc/Views/Project/Edit.cshtml b/Yavsc/Views/Project/Edit.cshtml index 096adc49..5aae58a0 100644 --- a/Yavsc/Views/Project/Edit.cshtml +++ b/Yavsc/Views/Project/Edit.cshtml @@ -51,7 +51,7 @@
- +
diff --git a/cli/src/Authentication/AuthException.cs b/cli/src/Authentication/AuthException.cs new file mode 100644 index 00000000..167f754a --- /dev/null +++ b/cli/src/Authentication/AuthException.cs @@ -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) + { + } + } +} \ No newline at end of file diff --git a/cli/src/Authentication/OAuthenticator.cs b/cli/src/Authentication/OAuthenticator.cs new file mode 100644 index 00000000..17655e44 --- /dev/null +++ b/cli/src/Authentication/OAuthenticator.cs @@ -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.Threading.Tasks.Task>; +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; + + /// + /// Gets the client identifier. + /// + /// The client identifier. + public string ClientId + { + get { return this.clientId; } + } + + /// + /// Gets the client secret. + /// + /// The client secret. + public string ClientSecret + { + get { return this.clientSecret; } + } + + /// + /// Gets the authorization scope. + /// + /// The authorization scope. + public string Scope + { + get { return this.scope; } + } + + /// + /// Gets the authorize URL. + /// + /// The authorize URL. + public Uri AuthorizeUrl + { + get { return this.authorizeUrl; } + } + + /// + /// Gets the access token URL. + /// + /// The URL used to request access tokens after an authorization code was received. + public Uri AccessTokenUrl + { + get { return this.accessTokenUrl; } + } + + /// + /// Redirect Url + /// + public Uri RedirectUrl + { + get { return this.redirectUrl; } + } + + /// + /// Initializes a new + /// that authenticates using implicit granting (token). + /// + /// + /// Client identifier. + /// + /// + /// Authorization scope. + /// + /// + /// Authorize URL. + /// + /// + /// Redirect URL. + /// + /// + /// Method used to fetch the username of an account + /// after it has been successfully authenticated. + /// + 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; + } + + /// + /// Initializes a new instance + /// that authenticates using authorization codes (code). + /// + /// + /// Client identifier. + /// + /// + /// Client secret. + /// + /// + /// Authorization scope. + /// + /// + /// Authorize URL. + /// + /// + /// Redirect URL. + /// + /// + /// URL used to request access tokens after an authorization code was received. + /// + /// + /// Method used to fetch the username of an account + /// after it has been successfully authenticated. + /// + 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; } } + + /// + /// Method that returns the initial URL to be displayed in the web browser. + /// + /// + /// A task that will return the initial URL. + /// + public Task 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(); + tcs.SetResult(url); + return tcs.Task; + } + + /// + /// Raised when a new page has been loaded. + /// + /// + /// URL of the page. + /// + /// + /// The parsed query of the URL. + /// + /// + /// The parsed fragment of the URL. + /// + protected void OnPageEncountered(Uri url, IDictionary query, IDictionary 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(); + tcs.SetResult(new Uri(cont)); + tcs.Task.RunSynchronously(); + } + return;*/ + // } + + var all = new Dictionary(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(); + } + /// + /// Raised when a new page has been loaded. + /// + /// + /// URL of the page. + /// + /// + /// The parsed query string of the URL. + /// + /// + /// The parsed fragment of the URL. + /// + protected void OnRedirectPageLoaded(Uri url, IDictionary query, IDictionary 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; + } + } + + /// + /// Asynchronously requests an access token with an authorization . + /// + /// + /// A dictionary of data returned from the authorization request. + /// + /// The authorization code. + /// Implements: http://tools.ietf.org/html/rfc6749#section-4.1 + Task> RequestAccessTokenAsync(string code) + { + var queryValues = new Dictionary { + { "grant_type", "authorization_code" }, + { "code", code }, + { "redirect_uri", RedirectUrl.AbsoluteUri }, + { "client_id", clientId } + }; + if (!string.IsNullOrEmpty(clientSecret)) + { + queryValues["client_secret"] = clientSecret; + } + + return RequestAccessTokenAsync(queryValues); + } + + /// + /// Asynchronously makes a request to the access token URL with the given parameters. + /// + /// The parameters to make the request with. + /// The data provided in the response to the access token request. + protected async Task> RequestAccessTokenAsync(IDictionary 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 FormDecode(string text) + { + + throw new NotImplementedException(); + } + + private IDictionary JsonDecode(string text) + { + return JsonConvert.DeserializeObject>(text); + } + + /// + /// Event handler that is fired when an access token has been retreived. + /// + /// + /// The retrieved account properties + /// + protected virtual void OnRetrievedAccountProperties(IDictionary 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 accountProperties) + { + throw new NotImplementedException(); + } + } + +} + + diff --git a/cli/Program.cs b/cli/src/Program.cs similarity index 100% rename from cli/Program.cs rename to cli/src/Program.cs diff --git a/cli/Services/EMailer.cs b/cli/src/Services/EMailer.cs similarity index 100% rename from cli/Services/EMailer.cs rename to cli/src/Services/EMailer.cs diff --git a/cli/Services/YaRazorEngineHost.cs b/cli/src/Services/YaRazorEngineHost.cs similarity index 100% rename from cli/Services/YaRazorEngineHost.cs rename to cli/src/Services/YaRazorEngineHost.cs diff --git a/cli/Startup.cs b/cli/src/Startup.cs similarity index 100% rename from cli/Startup.cs rename to cli/src/Startup.cs diff --git a/cli/YavscServerFactory.cs b/cli/src/YavscServerFactory.cs similarity index 100% rename from cli/YavscServerFactory.cs rename to cli/src/YavscServerFactory.cs