ASP.NET Core 2.0 open source Git HTTP Server, to achieve a similar GitHub, GitLab.
GitHub:https://github.com/linezero/GitServer
Set up
"GitSettings": {
"BasePath": "D:\\Git",
"GitPath": "git" }
Need to install Git, git commands can be executed and to ensure that, GitPath git may be the absolute path.
Currently implemented features
- Creating a warehouse
- Browse warehouse
- git client push pull
- Database supports SQLite, MSSQL, MySQL
- Support user management warehouse
More features you can view the readme, also welcome to contribute support.
Git interaction
LibGit2Sharp for operating Git library that implements create and delete read warehouse information warehouse.
Here are the main codes:
public Repository CreateRepository(string name) { string path = Path.Combine(Settings.BasePath, name); Repository repo = new Repository(Repository.Init(path, true)); return repo; } public Repository CreateRepository(string name, string remoteUrl) { var path = Path.Combine(Settings.BasePath, name); try { using (var repo = new Repository(Repository.Init(path, true))) { repo.Config.Set("core.logallrefupdates", true); repo.Network.Remotes.Add("origin", remoteUrl, "+refs/*:refs/*"); var logMessage = ""; foreach (var remote in repo.Network.Remotes) { IEnumerable<string> refSpecs = remote.FetchRefSpecs.Select(x => x.Specification); Commands.Fetch(repo, remote.Name, refSpecs, null, logMessage); } return repo; } } catch { try { Directory.Delete(path, true); } catch { } return null; } } public void DeleteRepository(string name) { Exception e = null; for(int i = 0; i < 3; i++) { try { string path = Path.Combine(Settings.BasePath, name); Directory.Delete(path, true); } catch(Exception ex) { e = ex; } } if (e != null) throw new GitException("Failed to delete repository", e); }
Git command execution
git-upload-pack
git-receive-pack
The main code GitCommandResult achieve IActionResult
public async Task ExecuteResultAsync(ActionContext context) { HttpResponse response = context.HttpContext.Response; Stream responseStream = GetOutputStream(context.HttpContext); string contentType = $"application/x-{Options.Service}"; if (Options.AdvertiseRefs) contentType += "-advertisement"; response.ContentType = contentType; response.Headers.Add("Expires", "Fri, 01 Jan 1980 00:00:00 GMT"); response.Headers.Add("Pragma", "no-cache"); response.Headers.Add("Cache-Control", "no-cache, max-age=0, must-revalidate"); ProcessStartInfo info = new ProcessStartInfo(_gitPath, Options.ToString()) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true }; using (Process process = Process.Start(info)) { GetInputStream(context.HttpContext).CopyTo(process.StandardInput.BaseStream); if (Options.EndStreamWithNull) process.StandardInput.Write('\0'); process.StandardInput.Dispose(); using (StreamWriter writer = new StreamWriter(responseStream)) { if (Options.AdvertiseRefs) { string service = $"# service={Options.Service}\n"; writer.Write($"{service.Length + 4:x4}{service}0000"); writer.Flush(); } process.StandardOutput.BaseStream.CopyTo(responseStream); } process.WaitForExit(); } }
BasicAuthentication achieve basic authentication
git http default authentication basic authentication to Basic, so here Basic achieve basic certification.
1.0 is some code can not be used until significant changes Authentication in ASP.NET Core 2.0.
First realize AuthenticationHandler, then realized AuthenticationSchemeOptions, create BasicAuthenticationOptions.
The most important is that these two categories, the following two classes for secondary classes, used to configure and middleware registration.
More to view official documents
Authentication
https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/
https://docs.microsoft.com/zh-cn/aspnet/core/migration/1x-to-2x/identity-2x
1 public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions> 2 { 3 public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 4 : base(options, logger, encoder, clock) 5 { } 6 protected async override Task<AuthenticateResult> HandleAuthenticateAsync() 7 { 8 if (!Request.Headers.ContainsKey("Authorization")) 9 return AuthenticateResult.NoResult(); 10 11 string authHeader = Request.Headers["Authorization"]; 12 if (!authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) 13 return AuthenticateResult.NoResult(); 14 15 string token = authHeader.Substring("Basic ".Length).Trim(); 16 string credentialString = Encoding.UTF8.GetString(Convert.FromBase64String(token)); 17 string[] credentials = credentialString.Split(':'); 18 19 if (credentials.Length != 2) 20 return AuthenticateResult.Fail("More than two strings seperated by colons found"); 21 22 ClaimsPrincipal principal = await Options.SignInAsync(credentials[0], credentials[1]); 23 24 if (principal != null) 25 { 26 AuthenticationTicket ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), BasicAuthenticationDefaults.AuthenticationScheme); 27 return AuthenticateResult.Success(ticket); 28 } 29 30 return AuthenticateResult.Fail("Wrong credentials supplied"); 31 } 32 protected override Task HandleForbiddenAsync(AuthenticationProperties properties) 33 { 34 Response.StatusCode = 403; 35 return base.HandleForbiddenAsync(properties); 36 } 37 38 protected override Task HandleChallengeAsync(AuthenticationProperties properties) 39 { 40 Response.StatusCode = 401; 41 string headerValue = $"{BasicAuthenticationDefaults.AuthenticationScheme} realm=\"{Options.Realm}\""; 42 Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, headerValue); 43 return base.HandleChallengeAsync(properties); 44 } 45 } 46 47 public class BasicAuthenticationOptions : AuthenticationSchemeOptions, IOptions<BasicAuthenticationOptions> 48 { 49 private string _realm; 50 51 public IServiceCollection ServiceCollection { get; set; } 52 public BasicAuthenticationOptions Value => this; 53 public string Realm 54 { 55 get { return _realm; } 56 set 57 { 58 _realm = value; 59 } 60 } 61 62 public async Task<ClaimsPrincipal> SignInAsync(string userName, string password) 63 { 64 using (var serviceScope = ServiceCollection.BuildServiceProvider().CreateScope()) 65 { 66 var _user = serviceScope.ServiceProvider.GetService<IRepository<User>>(); 67 var user = _user.List(r => r.Name == userName && r.Password == password).FirstOrDefault(); 68 if (user == null) 69 return null; 70 var identity = new ClaimsIdentity(BasicAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); 71 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); 72 var principal = new ClaimsPrincipal(identity); 73 return principal; 74 } 75 } 76 } 77 78 public static class BasicAuthenticationDefaults 79 { 80 public const string AuthenticationScheme = "Basic"; 81 } 82 public static class BasicAuthenticationExtensions 83 { 84 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) 85 => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, _ => { _.ServiceCollection = builder.Services;_.Realm = "GitServer"; }); 86 87 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicAuthenticationOptions> configureOptions) 88 => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); 89 90 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicAuthenticationOptions> configureOptions) 91 => builder.AddBasic(authenticationScheme, displayName: null, configureOptions: configureOptions); 92 93 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicAuthenticationOptions> configureOptions) 94 { 95 builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptions<BasicAuthenticationOptions>, BasicAuthenticationOptions>()); 96 return builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(authenticationScheme, displayName, configureOptions); 97 } 98 }
CookieAuthentication Cookie Authentication
Implementing custom login, no identity, log on to achieve registration.
The main code:
Enable Cookie
https://github.com/linezero/GitServer/blob/master/GitServer/Startup.cs#L60
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(options=> { options.AccessDeniedPath = "/User/Login"; options.LoginPath = "/User/Login"; })
log in
https://github.com/linezero/GitServer/blob/master/GitServer/Controllers/UserController.cs#L34
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Name)); identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); identity.AddClaim(new Claim(ClaimTypes.Email, user.Email)); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
Official document describes: https: //docs.microsoft.com/zh-cn/aspnet/core/security/authentication/cookie tabs = aspnetcore2x?
Deployment instructions
After the release configuration database and git directory (may be an absolute address and command), git repository directory.
{ "ConnectionStrings": { "ConnectionType": "Sqlite", //Sqlite,MSSQL,MySQL "DefaultConnection": "Filename=gitserver.db" }, "GitSettings": { "BasePath": "D:\\Git", "GitPath": "git" } }
After running up an account login account creation warehouses, then follow the prompts, then git push, git pull can.
>> Next Article: Orchard Core one minute to build ASP.NET Core CMS
ASP.NET Core 2.0 open source Git HTTP Server, to achieve a similar GitHub, GitLab.
GitHub:https://github.com/linezero/GitServer
Set up
"GitSettings": {
"BasePath": "D:\\Git",
"GitPath": "git" }
Need to install Git, git commands can be executed and to ensure that, GitPath git may be the absolute path.
Currently implemented features
- Creating a warehouse
- Browse warehouse
- git client push pull
- Database supports SQLite, MSSQL, MySQL
- Support user management warehouse
More features you can view the readme, also welcome to contribute support.
Git interaction
LibGit2Sharp for operating Git library that implements create and delete read warehouse information warehouse.
Here are the main codes:
public Repository CreateRepository(string name) { string path = Path.Combine(Settings.BasePath, name); Repository repo = new Repository(Repository.Init(path, true)); return repo; } public Repository CreateRepository(string name, string remoteUrl) { var path = Path.Combine(Settings.BasePath, name); try { using (var repo = new Repository(Repository.Init(path, true))) { repo.Config.Set("core.logallrefupdates", true); repo.Network.Remotes.Add("origin", remoteUrl, "+refs/*:refs/*"); var logMessage = ""; foreach (var remote in repo.Network.Remotes) { IEnumerable<string> refSpecs = remote.FetchRefSpecs.Select(x => x.Specification); Commands.Fetch(repo, remote.Name, refSpecs, null, logMessage); } return repo; } } catch { try { Directory.Delete(path, true); } catch { } return null; } } public void DeleteRepository(string name) { Exception e = null; for(int i = 0; i < 3; i++) { try { string path = Path.Combine(Settings.BasePath, name); Directory.Delete(path, true); } catch(Exception ex) { e = ex; } } if (e != null) throw new GitException("Failed to delete repository", e); }
Git command execution
git-upload-pack
git-receive-pack
The main code GitCommandResult achieve IActionResult
public async Task ExecuteResultAsync(ActionContext context) { HttpResponse response = context.HttpContext.Response; Stream responseStream = GetOutputStream(context.HttpContext); string contentType = $"application/x-{Options.Service}"; if (Options.AdvertiseRefs) contentType += "-advertisement"; response.ContentType = contentType; response.Headers.Add("Expires", "Fri, 01 Jan 1980 00:00:00 GMT"); response.Headers.Add("Pragma", "no-cache"); response.Headers.Add("Cache-Control", "no-cache, max-age=0, must-revalidate"); ProcessStartInfo info = new ProcessStartInfo(_gitPath, Options.ToString()) { UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true }; using (Process process = Process.Start(info)) { GetInputStream(context.HttpContext).CopyTo(process.StandardInput.BaseStream); if (Options.EndStreamWithNull) process.StandardInput.Write('\0'); process.StandardInput.Dispose(); using (StreamWriter writer = new StreamWriter(responseStream)) { if (Options.AdvertiseRefs) { string service = $"# service={Options.Service}\n"; writer.Write($"{service.Length + 4:x4}{service}0000"); writer.Flush(); } process.StandardOutput.BaseStream.CopyTo(responseStream); } process.WaitForExit(); } }
BasicAuthentication achieve basic authentication
git http default authentication basic authentication to Basic, so here Basic achieve basic certification.
1.0 is some code can not be used until significant changes Authentication in ASP.NET Core 2.0.
First realize AuthenticationHandler, then realized AuthenticationSchemeOptions, create BasicAuthenticationOptions.
The most important is that these two categories, the following two classes for secondary classes, used to configure and middleware registration.
More to view official documents
Authentication
https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/
https://docs.microsoft.com/zh-cn/aspnet/core/migration/1x-to-2x/identity-2x
1 public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions> 2 { 3 public BasicAuthenticationHandler(IOptionsMonitor<BasicAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 4 : base(options, logger, encoder, clock) 5 { } 6 protected async override Task<AuthenticateResult> HandleAuthenticateAsync() 7 { 8 if (!Request.Headers.ContainsKey("Authorization")) 9 return AuthenticateResult.NoResult(); 10 11 string authHeader = Request.Headers["Authorization"]; 12 if (!authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) 13 return AuthenticateResult.NoResult(); 14 15 string token = authHeader.Substring("Basic ".Length).Trim(); 16 string credentialString = Encoding.UTF8.GetString(Convert.FromBase64String(token)); 17 string[] credentials = credentialString.Split(':'); 18 19 if (credentials.Length != 2) 20 return AuthenticateResult.Fail("More than two strings seperated by colons found"); 21 22 ClaimsPrincipal principal = await Options.SignInAsync(credentials[0], credentials[1]); 23 24 if (principal != null) 25 { 26 AuthenticationTicket ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), BasicAuthenticationDefaults.AuthenticationScheme); 27 return AuthenticateResult.Success(ticket); 28 } 29 30 return AuthenticateResult.Fail("Wrong credentials supplied"); 31 } 32 protected override Task HandleForbiddenAsync(AuthenticationProperties properties) 33 { 34 Response.StatusCode = 403; 35 return base.HandleForbiddenAsync(properties); 36 } 37 38 protected override Task HandleChallengeAsync(AuthenticationProperties properties) 39 { 40 Response.StatusCode = 401; 41 string headerValue = $"{BasicAuthenticationDefaults.AuthenticationScheme} realm=\"{Options.Realm}\""; 42 Response.Headers.Append(Microsoft.Net.Http.Headers.HeaderNames.WWWAuthenticate, headerValue); 43 return base.HandleChallengeAsync(properties); 44 } 45 } 46 47 public class BasicAuthenticationOptions : AuthenticationSchemeOptions, IOptions<BasicAuthenticationOptions> 48 { 49 private string _realm; 50 51 public IServiceCollection ServiceCollection { get; set; } 52 public BasicAuthenticationOptions Value => this; 53 public string Realm 54 { 55 get { return _realm; } 56 set 57 { 58 _realm = value; 59 } 60 } 61 62 public async Task<ClaimsPrincipal> SignInAsync(string userName, string password) 63 { 64 using (var serviceScope = ServiceCollection.BuildServiceProvider().CreateScope()) 65 { 66 var _user = serviceScope.ServiceProvider.GetService<IRepository<User>>(); 67 var user = _user.List(r => r.Name == userName && r.Password == password).FirstOrDefault(); 68 if (user == null) 69 return null; 70 var identity = new ClaimsIdentity(BasicAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); 71 identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); 72 var principal = new ClaimsPrincipal(identity); 73 return principal; 74 } 75 } 76 } 77 78 public static class BasicAuthenticationDefaults 79 { 80 public const string AuthenticationScheme = "Basic"; 81 } 82 public static class BasicAuthenticationExtensions 83 { 84 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder) 85 => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, _ => { _.ServiceCollection = builder.Services;_.Realm = "GitServer"; }); 86 87 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, Action<BasicAuthenticationOptions> configureOptions) 88 => builder.AddBasic(BasicAuthenticationDefaults.AuthenticationScheme, configureOptions); 89 90 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, Action<BasicAuthenticationOptions> configureOptions) 91 => builder.AddBasic(authenticationScheme, displayName: null, configureOptions: configureOptions); 92 93 public static AuthenticationBuilder AddBasic(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<BasicAuthenticationOptions> configureOptions) 94 { 95 builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptions<BasicAuthenticationOptions>, BasicAuthenticationOptions>()); 96 return builder.AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>(authenticationScheme, displayName, configureOptions); 97 } 98 }
CookieAuthentication Cookie Authentication
Implementing custom login, no identity, log on to achieve registration.
The main code:
Enable Cookie
https://github.com/linezero/GitServer/blob/master/GitServer/Startup.cs#L60
services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(options=> { options.AccessDeniedPath = "/User/Login"; options.LoginPath = "/User/Login"; })
log in
https://github.com/linezero/GitServer/blob/master/GitServer/Controllers/UserController.cs#L34
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Name)); identity.AddClaim(new Claim(ClaimTypes.Name, user.Name)); identity.AddClaim(new Claim(ClaimTypes.Email, user.Email)); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
Official document describes: https: //docs.microsoft.com/zh-cn/aspnet/core/security/authentication/cookie tabs = aspnetcore2x?
Deployment instructions
After the release configuration database and git directory (may be an absolute address and command), git repository directory.
{ "ConnectionStrings": { "ConnectionType": "Sqlite", //Sqlite,MSSQL,MySQL "DefaultConnection": "Filename=gitserver.db" }, "GitSettings": { "BasePath": "D:\\Git", "GitPath": "git" } }
After running up an account login account creation warehouses, then follow the prompts, then git push, git pull can.