Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enterprise GitHub using Personal Tokens #251

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions BuildaGitServer/Base/Authentication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,21 @@ extension ProjectAuthenticator: KeychainStringSerializable {

let comps = value.componentsSeparatedByString(":")
guard comps.count >= 4 else { throw Error.withInfo("Corrupted keychain string") }
guard let service = GitService(rawValue: comps[0]) else {
throw Error.withInfo("Unsupported service: \(comps[0])")

var service: GitService
switch comps[0] {
case GitService.GitHub.hostname():
service = GitService.GitHub
case GitService.BitBucket.hostname():
service = GitService.BitBucket
default:
let host = comps[0]
guard let maybeService = GitService.createEnterpriseService(host) else {
throw Error.withInfo("Unsupported service: \(host)")
}
service = maybeService
}

guard let type = ProjectAuthenticator.AuthType(rawValue: comps[2]) else {
throw Error.withInfo("Unsupported auth type: \(comps[2])")
}
Expand All @@ -55,7 +67,7 @@ extension ProjectAuthenticator: KeychainStringSerializable {
public func toString() -> String {

return [
self.service.rawValue,
self.service.hostname(),
self.username,
self.type.rawValue,
self.secret
Expand Down
2 changes: 1 addition & 1 deletion BuildaGitServer/Base/BaseTypes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class SourceServerFactory {
public func createServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType {

if let auth = auth {
precondition(service == auth.service)
precondition(service.type() == auth.service.type())
}

return GitServerFactory.server(service, auth: auth)
Expand Down
4 changes: 4 additions & 0 deletions BuildaGitServer/Base/GitServerFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class GitServerFactory {
let baseURL = "https://api.github.com"
let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth)
server = GitHubServer(endpoints: endpoints, http: http)
case .EnterpriseGitHub:
let baseURL = "https://api.\(service.hostname())"
let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth)
server = GitHubServer(endpoints: endpoints, http: http)
case .BitBucket:
let baseURL = "https://api.bitbucket.org"
let endpoints = BitBucketEndpoints(baseURL: baseURL, auth: auth)
Expand Down
37 changes: 33 additions & 4 deletions BuildaGitServer/GitServerPublic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,88 @@ import Keys
import ReactiveCocoa
import Result

public enum GitService: String {
case GitHub = "github"
case BitBucket = "bitbucket"
public enum GitService {
case GitHub
case EnterpriseGitHub(host: String)
case BitBucket
// case GitLab = "gitlab"


public func type() -> String {
switch self {
case .GitHub: return "github"
case .EnterpriseGitHub: return "enterprisegithub"
case .BitBucket: return "bitbucket"
}
}

public func prettyName() -> String {
switch self {
case .GitHub: return "GitHub"
case .EnterpriseGitHub: return "EnterpriseGitHub"
case .BitBucket: return "BitBucket"
}
}

public func logoName() -> String {
switch self {
case .GitHub: return "github"
case .EnterpriseGitHub: return "enterprisegithub"
case .BitBucket: return "bitbucket"
}
}

public func hostname() -> String {
switch self {
case .GitHub: return "github.com"
case .EnterpriseGitHub(let host): return host
case .BitBucket: return "bitbucket.org"
}
}

public func authorizeUrl() -> String {
switch self {
case .GitHub: return "https:/login/oauth/authorize"
case .EnterpriseGitHub: assert(false)
case .BitBucket: return "https://bitbucket.org/site/oauth2/authorize"
}
}

public func accessTokenUrl() -> String {
switch self {
case .GitHub: return "https:/login/oauth/access_token"
case .EnterpriseGitHub: assert(false)
case .BitBucket: return "https://bitbucket.org/site/oauth2/access_token"
}
}

public func serviceKey() -> String {
switch self {
case .GitHub: return BuildasaurKeys().gitHubAPIClientId()
case .EnterpriseGitHub: assert(false)
case .BitBucket: return BuildasaurKeys().bitBucketAPIClientId()
}
}

public func serviceSecret() -> String {
switch self {
case .GitHub: return BuildasaurKeys().gitHubAPIClientSecret()
case .EnterpriseGitHub: assert(false)
case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret()
}
}

public static func createEnterpriseService(host: String) -> GitService? {
guard let url = NSURL(string: "http://\(host)") else { return nil }
do {
let response = try NSString.init(contentsOfURL: url, encoding: NSASCIIStringEncoding)
if response.lowercaseString.containsString("github") {
return GitService.EnterpriseGitHub(host: host)
}
} catch {
Log.error("\(error)")
}
return nil
}
}

public class GitServer : HTTPServer {
Expand Down
71 changes: 71 additions & 0 deletions BuildaGitServerTests/AuthenticationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// AuthenticationTests.swift
// Buildasaur
//
// Created by Rachel Caileff on 3/14/16.
// Copyright © 2016 Honza Dvorsky. All rights reserved.
//

import XCTest
@testable import BuildaGitServer

class AuthenticationTests: XCTestCase {

let gitService = "GIT"
let tokenType = "PersonalToken"
let tokenValue = "1234567890"

override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}

func testEmptyStringShouldThrow() {
do {
try ProjectAuthenticator.fromString("")
XCTFail()
} catch {
// Expected behavior
}
}

func testInvalidHostShouldThrow() {
do {
try ProjectAuthenticator.fromString("a:\(gitService):\(tokenType):\(tokenValue)")
XCTFail()
} catch {
// Expected behavior
}
}

func testNonexistantHostShouldThrow() {
do {
try ProjectAuthenticator.fromString("some.fakehostname.com:\(gitService):\(tokenType):\(tokenValue)")
XCTFail()
} catch {
// Expected behavior
}
}

func testInvalidAuthTypeShouldThrow() {
do {
try ProjectAuthenticator.fromString("\(GitService.GitHub.hostname()):\(gitService):junkstring:\(tokenValue)")
XCTFail()
} catch {
// Expected behavior
}
}

func testValidStringShouldNotThrow() {
do {
try ProjectAuthenticator.fromString("\(GitService.GitHub.hostname()):\(gitService):\(tokenType):\(tokenValue)")
} catch {
XCTFail()
}
}
}
98 changes: 98 additions & 0 deletions BuildaGitServerTests/EnterpriseGitHubSourceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// EnterpriseGitHubSourceTests.swift
// Buildasaur
//
// Created by Rachel Caileff on 3/8/16.
// Copyright © 2016 Honza Dvorsky. All rights reserved.
//

import Cocoa
import XCTest
@testable import BuildaGitServer
import BuildaUtils

class EnterpriseGitHubSourceTests: XCTestCase {

var github: GitHubServer!
var repo: String = "my/repo" // TODO: fill in accessible enterprise github repo

override func setUp() {
super.setUp()

self.github = GitServerFactory.server(.EnterpriseGitHub(host: "git.mycompany.com"), auth: nil) as! GitHubServer // TODO: fill in accessible enterprise github host
}

override func tearDown() {

self.github = nil

super.tearDown()
}

func tryEndpoint(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, completion: (body: AnyObject!, error: NSError!) -> ()) {

let expect = expectationWithDescription("Waiting for url request")

do {
let request = try self.github.endpoints.createRequest(method, endpoint: endpoint, params: params)

self.github.http.sendRequest(request, completion: { (response, body, error) -> () in

completion(body: body, error: error)
expect.fulfill()
})

waitForExpectationsWithTimeout(10, handler: nil)
} catch {
XCTFail()
}
}

// func testCreateEnterpriseServiceShouldReturnNilWhenPassedAnInvalidHostname() {
// let gitService = GitService.createEnterpriseService("some.fakehostname.com")
// XCTAssertNil(gitService)
// }
//
// func testCreateEnterpriseServiceShouldReturnAGitServiceWhenPassedAValidHostname() {
// let gitService = GitService.createEnterpriseService("github.com")
// XCTAssertNotNil(gitService)
// }
//
// func testGetPullRequests() {
//
// let params = [
// "repo": self.repo
// ]
//
// self.tryEndpoint(.GET, endpoint: .PullRequests, params: params) { (body, error) -> () in
//
// XCTAssertNotNil(body, "Body must be non-nil")
// if let body = body as? NSArray {
// let prs: [GitHubPullRequest]? = try? GitHubArray(body)
// XCTAssertGreaterThan(prs?.count ?? -1, 0, "We need > 0 items to test parsing")
// Log.verbose("Parsed PRs: \(prs)")
// } else {
// XCTFail("Body nil")
// }
// }
// }
//
// func testGetBranches() {
//
// let params = [
// "repo": self.repo
// ]
//
// self.tryEndpoint(.GET, endpoint: .Branches, params: params) { (body, error) -> () in
//
// XCTAssertNotNil(body, "Body must be non-nil")
// if let body = body as? NSArray {
// let branches: [GitHubBranch]? = try? GitHubArray(body)
// XCTAssertGreaterThan(branches?.count ?? -1, 0, "We need > 0 items to test parsing")
// Log.verbose("Parsed branches: \(branches)")
// } else {
// XCTFail("Body nil")
// }
// }
// }
}
2 changes: 1 addition & 1 deletion BuildaKit/SyncerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ extension SyncerManager: HeartbeatManagerDelegate {
public func typesOfRunningSyncers() -> [String : Int] {
return self.syncers.filter { $0.active }.reduce([:]) { (all, syncer) -> [String: Int] in
var stats = all
let syncerType = syncer._project.workspaceMetadata!.service.rawValue
let syncerType = syncer._project.workspaceMetadata!.service.type()
stats[syncerType] = (stats[syncerType] ?? 0) + 1
return stats
}
Expand Down
6 changes: 5 additions & 1 deletion BuildaKit/WorkspaceMetadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ extension WorkspaceMetadata {
} else if projectURLString.containsString(GitService.BitBucket.hostname()) {
gitService = .BitBucket
} else {
Log.error("This git service is not yet supported.")
var urlPieces = projectURLString.split(":")
gitService = GitService.createEnterpriseService(urlPieces[0])
if gitService == nil {
Log.error("This git service is not yet supported.")
}
}

switch url.scheme {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:czechboy0\/Buildasaur.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git@github.com:czechboy0\/Buildasaur.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1C5C2A17EEADA6DBF6678501245487A71FBE28BB"
}
Expand Down
2 changes: 1 addition & 1 deletion BuildaKitTests/WorkspaceMetadataTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class WorkspaceMetadataTests: XCTestCase {
}

expect(checkoutType) == expectedCheckoutType
expect(service) == expectedGitService
expect(service.type()) == expectedGitService.type()
}

// MARK: GitHub
Expand Down
Loading