aboutsummaryrefslogtreecommitdiff
path: root/gpg/skeybase/src/main/scala/com/github/jodersky/skeybase/verification/Verifier.scala
blob: 6025fef1eaa66a1a3fa0e8f5eb1ea8397818afea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.github.jodersky.skeybase
package verification

import scala.language.implicitConversions

import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.util.Failure
import scala.util.Success
import scala.util.Try

import com.github.jodersky.skeybase.Proof
import com.github.jodersky.skeybase.PublicKey

import akka.actor.ActorSystem
import spray.http.HttpHeaders.Location
import spray.http.HttpRequest
import spray.http.HttpResponse
import spray.http.Uri
import spray.json.DefaultJsonProtocol
import spray.json.JsonParser
import spray.json.ParserInput.apply

trait Verifier {

  def verify(fingerprint: String, proof: Proof)(implicit sys: ActorSystem): Future[Proof]

}

object Verifier {

  object JsonProtocol extends DefaultJsonProtocol {
    implicit val serviceFormat = jsonFormat2(Service.apply)
    implicit val keyFormat = jsonFormat1(PublicKey.apply)
    implicit val statementBodyFormat = jsonFormat2(StatementBody.apply)
    implicit val statementFormat = jsonFormat1(Statement.apply)
  }
  import JsonProtocol._

  implicit def tryToFuture[A](t: Try[A]): Future[A] = t match {
    case Success(a) => Future.successful(a)
    case Failure(e) => Future.failed(e)
  }

  def withRedirects(
    sendReceive: HttpRequest => Future[HttpResponse],
    maxRedirects: Int = 5)(implicit ec: ExecutionContext): HttpRequest => Future[(Uri, HttpResponse)] = { request =>

    def dispatch(request: HttpRequest, redirectsLeft: Int): Future[(Uri, HttpResponse)] = if (redirectsLeft <= 0) {
      Future.failed(new RuntimeException("Too many redirects."))
    } else {
      sendReceive(request).flatMap { response =>
        if (response.status.value.startsWith("3")) {
          response.header[Location].map { location =>
            dispatch(request.copy(uri = location.uri), redirectsLeft - 1)
          } getOrElse {
            Future.failed(new RuntimeException("Missing location header in redirect response."))
          }
        } else {
          Future.successful(request.uri, response)
        }
      }
    }

    dispatch(request, maxRedirects)
  }

  def finalHost(host: String) = (uri: Uri, response: HttpResponse) => {
    if (uri.authority.host.address != host)
      throw new VerificationException("Final host is not " + host)
    else
      response
  }

  def extractSignedStatement(content: String): Try[String] = Try {
    val regex = """(-----BEGIN PGP MESSAGE-----(.|\n)*-----END PGP MESSAGE-----?)""".r
    regex.findFirstIn(content) getOrElse {
      throw new VerificationException("No OpenPGP message found.")
    }
  }

  def verifyStatement(statement: String, service: String, username: String): Try[String] = Try {
    val stmt = JsonParser(statement).convertTo[Statement]

    if (stmt.body.service.name != service) throw new VerificationException(
      "The service specified in the signed statement (" + stmt.body.service.name + ") is not " +
        "the same as the service under which the statement was found (" + service + ")")
    else if (stmt.body.service.username != username) throw new VerificationException(
      "The username specified in the signed statement (" + stmt.body.service.username + ") is not " +
        "the same as the username under which the statement was found (" + username + ")")
    else statement

  }

  /*
   * if (!(uri.path.tail startsWith (Path(proof.nametag)))) {
   *   throw new VerificationException("Final github account does not match the one provided in the proof." + uri.path.head)
   * }
   

  def extractHtmlId(id: String, html: String): Option[String] = {
    val cleaner = new HtmlCleaner
    val root = cleaner.clean(html)
    root.getElementsByName("div", true).find(_.getAttributeByName("id") == id).map { div =>
      StringEscapeUtils.unescapeHtml4(div.getText.toString())
    }
  }*/

}