Handle retrieving authorization codes

Added authTokens.dart to connect to Spicerack and Google OAuth2 servers
in order to get access tokens prior to launching Oregano.

Allow developers (developers developers) to add/override url parameters
with /tmp/client_params

Change-Id: I619f24d1fe5e167784e0cff39063709d46046448
diff --git a/package/google/google_oregano/authTokens.dart b/package/google/google_oregano/authTokens.dart
new file mode 100644
index 0000000..0a51ae1
--- /dev/null
+++ b/package/google/google_oregano/authTokens.dart
@@ -0,0 +1,111 @@
+/// Authorize client with Spicerack and OAuth2.0
+
+import 'dart:async';
+import 'dart:io';
+import 'dart:convert';
+
+const String SPICERACK_AUTH =
+    "https://www-googleapis-staging.sandbox.google.com/rpc";
+
+const String separatorFlag = "--separator=";
+
+main(List<String> args) {
+  int now = new DateTime.now().millisecondsSinceEpoch;
+
+  String separator = "&";
+  // I don't want to import a 3rd party arguments parser for a simple script.
+  // Allow the caller to switch between '&flag=' and ' --flag='
+  if (args.length > 0 && args[0].startsWith(separatorFlag) ) {
+    separator = args[0].substring(separatorFlag.length);
+  }
+
+  String serialNumber;
+  ProcessResult rslt = Process.runSync("serial", []);
+  serialNumber = rslt.stdout.trim();
+
+  makeSignature(now).then((String signature) {
+    getAuthCode(serialNumber, signature). then((authResponse) {
+      Map json = JSON.decode(authResponse)['result'];
+      var oauthClientId = json['oauthClientId'];
+      var oauthAddress = json['oauthAddress'];
+      var oauthClientSecret = json['oauthClientSecret'];
+      getOAuth2Tokens(json['authorizationCode'], oauthAddress, oauthClientId,
+          oauthClientSecret).then((tokens) {
+        Map json = JSON.decode(tokens);
+        print(
+            "${separator}clientId=$oauthClientId"
+            "${separator}clientSecret=$oauthClientSecret"
+            "${separator}oauth2Url=$oauthAddress"
+            "${separator}refreshToken=${json['refresh_token']}");
+      });
+    });
+  });
+}
+
+
+/**
+ * Returns the [now], signed with the local device certificate, and base64
+ * encoded. See [getAuthCode]. This is equivalent to the following:
+ *     $(date +%s | openssl rsautl -sign -inkey /etc/ssl/private/device.key | openssl base64 -A)
+ */
+Future<String> makeSignature(int now) {
+  return Process.start("openssl",
+      ["rsautl", "-sign", "-inkey", "/etc/ssl/private/device.key"])
+        .then((Process sign) {
+          sign.stdin.write("$now");
+          sign.stdin.close();
+          return Process.start("openssl", ["base64", "-A"])
+              .then((Process base64) {
+                sign.stdout.pipe(base64.stdin);
+                return base64.stdout.transform(new Utf8Decoder()).join()
+                   .then((String encOut) => encOut);
+              });
+        });
+}
+
+
+/**
+ * Connect to Spicerack and request an authcode. Spicerack fetches the client
+ * certificate from CARS based on the [serial] number and then verifies the
+ * [signature] is valid.
+ */
+Future<String> getAuthCode(String serial, String signature) {
+  var map = {
+    "method": "spicerack.authcode.fetch",
+    "apiVersion": "v1",
+    "params": {
+      "serialNumber": serial,
+      "signature": signature,
+    }
+  };
+  String authRequest = JSON.encode(map);
+  return new HttpClient().postUrl(Uri.parse(SPICERACK_AUTH))
+      .then((HttpClientRequest request) {
+        request.headers.contentType = new ContentType("application", "json");
+        request.write(authRequest);
+        return request.close();
+      }).then((HttpClientResponse response) =>
+          response.transform(new Utf8Decoder()).join());
+}
+
+
+
+/**
+ * Connect to the Oath2 service to get access tokens.
+ */
+Future<String> getOAuth2Tokens(String authCode, String authAddress,
+    String authClientId, String authClientSecret) {
+  String tokenRequest = "code=$authCode"
+      "&client_id=$authClientId"
+      "&client_secret=$authClientSecret"
+      "&redirect_uri=oob"
+      "&grant_type=authorization_code";
+  return new HttpClient().postUrl(Uri.parse(authAddress))
+      .then((HttpClientRequest request) {
+        request.headers.contentType =
+            new ContentType("application", "x-www-form-urlencoded");
+        request.write(tokenRequest);
+        return request.close();
+      }).then((HttpClientResponse response) =>
+          response.transform(new Utf8Decoder()).join());
+}
diff --git a/package/google/google_oregano/google_oregano.mk b/package/google/google_oregano/google_oregano.mk
index aaf626e..813b2f8 100644
--- a/package/google/google_oregano/google_oregano.mk
+++ b/package/google/google_oregano/google_oregano.mk
@@ -7,7 +7,8 @@
 
 define GOOGLE_OREGANO_INSTALL_TARGET_CMDS
 	mkdir -p $(TARGET_DIR)/app/oregano/
-	$(INSTALL) -m 0755 -D package/google/google_oregano/S99oregano $(TARGET_DIR)/etc/init.d/S99oregano;
+	$(INSTALL) -m 0755 -D package/google/google_oregano/S99oregano $(TARGET_DIR)/etc/init.d/S99oregano
+	$(INSTALL) -m 0755 -D package/google/google_oregano/authTokens.dart $(TARGET_DIR)/app/oregano/authTokens.dart
 	$(INSTALL) -D -m 0755 package/google/google_oregano/runoregano $(TARGET_DIR)/app/oregano/runoregano
 	cp -af $(@D)/* $(TARGET_DIR)/app/oregano/
 endef
diff --git a/package/google/google_oregano/runoregano b/package/google/google_oregano/runoregano
index 0a0c6aa..d859339 100755
--- a/package/google/google_oregano/runoregano
+++ b/package/google/google_oregano/runoregano
@@ -5,8 +5,7 @@
 cd "$(dirname "$0")"
 
 export LD_LIBRARY_PATH=/app/client:/usr/local/lib:$LD_LIBRARY_PATH
-export SERIALNUMBER=$(hnvram -qr 1ST_SERIAL_NUMBER)
-[ -z "$SERIALNUMBER" ] && SERIALNUMBER=$(hnvram -qr SERIAL_NO)
+export SERIALNUMBER=$(serial)
 
 ulimit -c 49152
 
@@ -16,32 +15,37 @@
   babysit 10 \
       dart -p . isp/fiber/marjoram/src/basil.dart 2>&1 | logos basil 0 20000000 &
 
-  #TODO(codefu): make this url less hard-coded.
   cd /usr/local/bin/webkitGl2
 
-  read epoch < /tmp/client_epoch && PARAMS="${PARAMS}&epoch=$epoch"
-  read screenHost < /tmp/screeenHost && PARAMS="${PARAMS}&screenhost=$screenHost"
-
-  #TODO(codefu): Handle authentication.
-
-  #TODO(codefu): Ask spicerack for this either before, or have Oregano query.
+  #TODO(codefu): Have oregano query the storage box from Spicerack
   storageHost="192.168.4.6"
   read storageHost < /tmp/storageHost
-  PARAMS="${PARAMS}&storageHost=$storageHost"
+  PARAMS="storageHost=$storageHost"
   PARAMS="${PARAMS}&tvBoxSerial=$SERIALNUMBER"
 
-  echo Oregano params = $PARAMS
+  #Authorize client with the backend.
+  PARAMS="$PARAMS"$(dart /app/oregano/authTokens.dart)
+
+  #Allow developer overriding default parameters, as they are tagged on last they
+  #will override all matching settings above.
+  read devParams  < /tmp/client_params
+  PARAMS="$PARAMS&$devParams"
+
   #TODO(codefu) use Spicerack redirector when its implemented.
-  address="https://fiber.google.com/oregano/3/oregano.html"
+  address="https://fiber.google.com/oregano/4/oregano.html"
   read address < /tmp/client_address
   address="$address?$PARAMS"
-  echo Oregano addres = $address
+
+  echo Oregano address = $address
   babysit 10 \
       ./start --allow-unsecure-content --remote-debugging-port=9222 --disable-web-security "${address}" 2>&1 | logos oregano 0 20000000 &
 else
   start_sagesrv
 
+  #Authorize server with the backend.
+  PARAMS=$(dart /app/oregano/authTokens.dart --separator=" --")
+  echo marjoram PARAMS=$PARAMS
   # Start up the scheduler / websocket server / spicerack proxy
   babysit 10 \
-      setuid video:video dart -p . isp/fiber/marjoram/src/marjoram.dart 2>&1 | logos marjoram 0 20000000 &
+      setuid video:video dart -p . isp/fiber/marjoram/src/marjoram.dart $PARAMS 2>&1 | logos marjoram 0 20000000 &
 fi