package mesosphere.dcos.client.model; import java.io.IOException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Objects; import com.nimbusds.jose.JOSEException; import com.nimbusds.jose.JOSEObjectType; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.crypto.RSASSASigner; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import mesosphere.client.common.ModelUtils; import net.oauth.signature.pem.PEMReader; public class DCOSAuthCredentials { // Note that these member variables need to remain named this way, as they are serialized with these names in the // DCOSClient#authenticate(...) call. private final String uid; private final String password; private final String token; private DCOSAuthCredentials(final String uid, final String password, final String servicePrivateKey) { this.uid = uid; this.password = password; this.token = servicePrivateKey; } /** * @param serviceId service account id * @param servicePrivateKey contents of the private key (pem) file for the service account * @return auth credentials for DC/OS */ public static DCOSAuthCredentials forServiceAccount(final String serviceId, final String servicePrivateKey) { try { final String serviceLoginToken = signJWT(serviceId, parsePrivateKey(servicePrivateKey)); return new DCOSAuthCredentials(serviceId, null, serviceLoginToken); } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { throw new RuntimeException(e); } } public static DCOSAuthCredentials forUserAccount(final String userId, final String password) { return new DCOSAuthCredentials(userId, password, null); } public String getUid() { return uid; } public String getPassword() { return password; } public String getServiceLoginToken() { return token; } private static PrivateKey parsePrivateKey(final String key) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(new PEMReader(key.getBytes()).getDerBytes()); // I assume RSA is always what DC/OS uses since examples in their doc don't show other algorithms // and so this is not configurable final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } private static String signJWT(String uid, PrivateKey privateKey) { final JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).type(JOSEObjectType.JWT).build(); final JWTClaimsSet payload = new JWTClaimsSet.Builder().claim("uid", uid).build(); final SignedJWT signedJWT = new SignedJWT(header, payload); try { signedJWT.sign(new RSASSASigner(privateKey)); return signedJWT.serialize(); } catch (JOSEException e) { throw new RuntimeException(e); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DCOSAuthCredentials that = (DCOSAuthCredentials) o; return Objects.equals(uid, that.uid) && Objects.equals(password, that.password) && Objects.equals(token, that.token); } @Override public int hashCode() { return Objects.hash(uid, password, token); } @Override public String toString() { return ModelUtils.toString(this); } }