Skip to content
Open
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ RUN apt-get update && apt-get upgrade -y

RUN apt-get install -y maven git nano

RUN git clone https://github.com/matlink/token-dispenser.git /token-dispenser
ADD . /token-dispenser

RUN groupadd -g 666 dispenser && \
useradd -m -g dispenser -u 666 -s /bin/bash dispenser && \
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ Two things are configurable:

Token dispenser uses [spark framework](http://sparkjava.com/). To configure network address and port on which spark should listen change `spark-host` and `spark-port`.

Basic auth is also available. To enable, change `basic-auth` to `<user>:<pass>`.

#### Storage

There are three storage options supported:
* **Plain text** Set `storage` to `plaintext` to use it. `storage-plaintext-path` property is used to store filesystem path to a plain text file with email-password pairs. There is an example [here](/passwords/passwords.txt). Securing it is up to you.
* **MongoDB** Set `storage` to `mongodb` to use it. Configurable parameters are self-explanatory.
* **Environment** Set `storage` to `env` to use it. Set the environment variables `TOKEN_EMAIL` and `TOKEN_PASSWORD` before starting Token dispenser. (Only one email/account is supported.)
* **Environment** Set `storage` to `env` to use it. Set the environment variable `TOKEN_CREDENTIALS` before starting Token dispenser. `TOKEN_CREDENTIALS` takes URL-encoded, comma-separated pairs of emails and passwords. Each pair must contain a colon to separate the email and password. For example, `TOKEN_CREDENTIALS=myemail%40gmail.com:password,myotheremail%40yahoo.com:1234`.

### Usage
Once server is configured, you can get the tokens for **regular requests** at http://server-address:port/token/email/youremail@gmail.com
Expand All @@ -47,3 +49,4 @@ gplaycli requires also the GSFid. Token and GSFid can be retrieved using http://

* [play-store-api](https://github.com/yeriomin/play-store-api)
* [spark](http://sparkjava.com/)

Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
package com.github.yeriomin.tokendispenser;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;

public class PasswordsDbEnv implements PasswordsDbInterface {
private String email;
private String password;

static private final String FIELD_SEPARATOR = ":";
static private final String LINE_SEPARATOR = ",";

private Map<String, String> passwords = new HashMap<>();

PasswordsDbEnv(Properties config) {
email = System.getenv(Server.ENV_EMAIL);
password = System.getenv(Server.ENV_PASSWORD);
if (email == null || password == null) {
throw new IllegalArgumentException("empty email/password, make sure to set " + Server.ENV_EMAIL + " and " + Server.ENV_PASSWORD);

String envString = System.getenv(Server.ENV_TOKEN_CREDENTIALS);
String[] lines = envString.split(LINE_SEPARATOR);
for (String line : lines) {
String[] pair = line.split(FIELD_SEPARATOR);
if (pair.length != 2) {
Server.LOG.warn("Invalid user:pass pair in " + Server.ENV_TOKEN_CREDENTIALS);
continue;
}
try {
String email = URLDecoder.decode(pair[0], StandardCharsets.UTF_8.name());
String password = URLDecoder.decode(pair[1], StandardCharsets.UTF_8.name());
passwords.put(email, password);
} catch (UnsupportedEncodingException e) {
Server.LOG.error("UTF-8 is unsupported.");
return;
}
}

}

@Override
public String getRandomEmail() {
return email;
List<String> emails = new ArrayList<>(passwords.keySet());
return emails.get(new Random().nextInt(emails.size()));
}

@Override
public String get(String email) {
if (!email.equals(this.email)) {
throw new IllegalArgumentException("invalid email: " + email);
}
return password;
Server.LOG.info(email + (passwords.containsKey(email) ? "" : " NOT") + " found");
return passwords.get(email);
}

@Override
Expand Down
47 changes: 44 additions & 3 deletions src/main/java/com/github/yeriomin/tokendispenser/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.Properties;

import static spark.Spark.after;
import static spark.Spark.before;
import static spark.Spark.get;
import static spark.Spark.halt;
import static spark.Spark.ipAddress;
import static spark.Spark.notFound;
import static spark.Spark.port;
Expand All @@ -31,13 +37,13 @@ public class Server {
static final String PROPERTY_MONGODB_DB = "mongodb-databaseNameStorage";
static final String PROPERTY_MONGODB_COLLECTION = "mongodb-collectionName";
static final String PROPERTY_EMAIL_RETRIEVAL = "enable-email-retrieval";
static final String PROPERTY_BASIC_AUTH = "basic-auth";

static public final String STORAGE_MONGODB = "mongodb";
static public final String STORAGE_PLAINTEXT = "plaintext";
static public final String STORAGE_ENV = "env";

static public final String ENV_EMAIL = "TOKEN_EMAIL";
static public final String ENV_PASSWORD = "TOKEN_PASSWORD";

static public final String ENV_TOKEN_CREDENTIALS = "TOKEN_CREDENTIALS";

static PasswordsDbInterface passwords;

Expand All @@ -59,7 +65,34 @@ public static void main(String[] args) {
res.header("Access-Control-Request-Method", "GET");
});
after((req, res) -> res.type("text/plain"));
String basicAuth = config.getProperty(PROPERTY_BASIC_AUTH, "");
if (!basicAuth.equals("")) {
try {
String[] pair = basicAuth.split(":");
if (pair.length != 2) {
LOG.error(PROPERTY_BASIC_AUTH + " not in the format '<user>:<pass>'.");
return;
}
String user = URLDecoder.decode(pair[0], StandardCharsets.UTF_8.name());
String pass = URLDecoder.decode(pair[1], StandardCharsets.UTF_8.name());
before((req, res) -> {
if (req.pathInfo().equals("/health")) return;
String header = req.headers("Authorization");
if (header == null) halt(401, "Access denied.");
String[] parts = header.split(" ");
if (parts.length != 2) halt(400, "Malformed auth header.");
if (!parts[0].equals("Basic")) halt(401, "Unsupported auth method.");
String[] creds = new String(Base64.getDecoder().decode(parts[1])).split(":");
if (creds.length != 2) halt(400, "Malformed auth header.");
if (!creds[0].equals(user) || !creds[1].equals(pass)) halt(401, "Access denied.");
});
} catch (UnsupportedEncodingException e) {
Server.LOG.error("UTF-8 is unsupported.");
return;
}
}
Server.passwords = PasswordsDbFactory.get(config);
get("/health", (req, res) -> "");
get("/token/email/:email", (req, res) -> new TokenResource().handle(req, res));
get("/token-ac2dm/email/:email", (req, res) -> new TokenAc2dmResource().handle(req, res));
if (config.getProperty(PROPERTY_EMAIL_RETRIEVAL, "false").equals("true")) {
Expand All @@ -84,6 +117,14 @@ static Properties getConfig() {
properties.put(PROPERTY_MONGODB_PASSWORD, System.getenv("OPENSHIFT_MONGODB_DB_PASSWORD"));
properties.put(PROPERTY_MONGODB_DB, System.getenv("OPENSHIFT_APP_NAME"));
}
String basicAuth = System.getenv(PROPERTY_BASIC_AUTH.replace("-", "_").toUpperCase());
if (basicAuth != null) {
properties.put(PROPERTY_BASIC_AUTH, basicAuth);
}
String storage = System.getenv(PROPERTY_STORAGE.toUpperCase());
if (Arrays.asList(STORAGE_MONGODB, STORAGE_PLAINTEXT, STORAGE_ENV).contains(storage)) {
properties.put(PROPERTY_STORAGE, storage);
}
return properties;
}
}
2 changes: 2 additions & 0 deletions src/main/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ mongodb-password=pwd
mongodb-databaseNameStorage=token-dispenser
mongodb-collectionName=passwords
enable-email-retrieval=true
basic-auth=