Skip to content

Commit

Permalink
Rewrite parser for win-storage from js to ts and simplify it
Browse files Browse the repository at this point in the history
Use wrapper for win-credstore-parser,because Transform isn't injectable.
In previous implementation each entity was separated on lines but
currently we get chunks with all lines for each entity.
I have to extends Transform this way because of:
microsoft/TypeScript#17032
  • Loading branch information
rugpanov committed Dec 18, 2017
1 parent 27a9970 commit d1878e7
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 143 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@
"xmlrpc": "^1.3.2",
"underscore": "^1.8.3",
"event-stream": "^3.3.2",
"path": "^0.12.7"
"path": "^0.12.7",
"readable-stream": "^2.3.3"
}
}
106 changes: 0 additions & 106 deletions src/bll/credentialsstore/win32/win-credstore-parser.js

This file was deleted.

77 changes: 77 additions & 0 deletions src/bll/credentialsstore/win32/win-credstore-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use strict";

const stream = require("readable-stream");
const Transform = stream.Transform;
import {injectable} from "inversify";

@injectable()
export class WinCredStoreParsingStreamWrapper {
private readonly instance: WinCredStoreParsingStream;

constructor() {
this.instance = new WinCredStoreParsingStream();
}

public get parser(): WinCredStoreParsingStream {
return this.instance;
}
}

export class WinCredStoreParsingStream extends (Transform as { new(): any; }) {

private static parseInputFieldsRegExp: RegExp = /^([^:]+):\s(.*)$/m;

constructor() {
super();
Transform.call(this, {
objectMode: true
});
this.currentEntry = undefined;
}

public _transform(chunk, encoding, callback) {
const lines = chunk.toString();

if (!lines) {
callback();
}

if (WinCredStoreParsingStream.isNewData(lines)) {
this.ensureCurrentEntryInitialized();
lines.split("\n").forEach((line) => {
const match = WinCredStoreParsingStream.parseInputFieldsRegExp.exec(line);
if (match) {
const key = WinCredStoreParsingStream.separateWordsToCamelCase(match[1]);
const value = match[2];
this.currentEntry[key] = value;
}
});
}

callback();
}

private static isNewData(line: string): boolean {
return line !== "";
}

private ensureCurrentEntryInitialized() {
if (!this.currentEntry) {
this.currentEntry = {};
}
}

private static separateWordsToCamelCase(fieldName: string): string {
const parts = fieldName.split(" ");
parts[0] = parts[0].toLowerCase();
return parts.join("");
}

public _flush(callback) {
if (this.currentEntry) {
this.push(this.currentEntry);
this.currentEntry = undefined;
}
callback();
}
}
43 changes: 9 additions & 34 deletions src/bll/credentialsstore/win32/win-credstore.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
"use strict";

import {Stream} from "stream";
import {injectable} from "inversify";
import {inject, injectable} from "inversify";
import {WinCredStoreParsingStream, WinCredStoreParsingStreamWrapper} from "./win-credstore-parser";
import {TYPES} from "../../utils/constants";
const childProcess = require("child_process");
const es = require("event-stream");
const path = require("path");
const parser = require("./win-credstore-parser");
const credExePath = path.join(__dirname, "../bin/win32/creds.exe");

@injectable()
export class WinPersistentCredentialsStore {
private targetNamePrefix: string = "";
private parser: WinCredStoreParsingStream;

public constructor(@inject(TYPES.WinCredStoreParsingStreamWrapper) wrapper: WinCredStoreParsingStreamWrapper) {
this.parser = wrapper.parser;
}

public setPrefix(prefix: string): void {
this.targetNamePrefix = prefix;
Expand All @@ -30,44 +36,13 @@ export class WinPersistentCredentialsStore {
public getCredentialsListStream(): Stream {
const credsProcess = childProcess.spawn(credExePath, ["-s", "-g", "-t", this.targetNamePrefix + "*"]);
return credsProcess.stdout
.pipe(parser())
.pipe(this.parser)
.pipe(es.mapSync((cred) => {
cred.targetName = this.removePrefix(cred.targetName);
return cred;
}));
}

public get(targetName: string): Promise<string> {
const args = [
"-s",
"-t", this.ensurePrefix(targetName)
];

const credsProcess = childProcess.spawn(credExePath, args);
let result = undefined;
const errors = [];

credsProcess.stdout.pipe(parser())
.on("data", function (credential) {
result = credential;
result.targetName = this.removePrefix(result.targetName);
});

credsProcess.stderr.pipe(es.split())
.on("data", (line) => {
errors.push(line);
});
return new Promise<string>((resolve, reject) => {
credsProcess.on("exit", (code) => {
if (code === 0) {
resolve(result);
} else {
reject("Getting credential failed, exit code " + code + ": " + errors.join(", "));
}
});
});
}

public set(targetName: string, password: string): Promise<void> {
const passwordBuffer: Buffer = new Buffer(password, "utf8");
const args = [
Expand Down
3 changes: 2 additions & 1 deletion src/bll/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ export const TYPES = {
ProviderManager: Symbol("ProviderManager"),
ResourceProvider: Symbol("ChangesProvider"),
BuildProvider: Symbol("BuildProvider"),
LinuxFileApi: Symbol("LinuxFileApi")
LinuxFileApi: Symbol("LinuxFileApi"),
WinCredStoreParsingStreamWrapper: Symbol("WinCredStoreParsingStreamWrapper")
};

export enum CvsOperation {
Expand Down
2 changes: 2 additions & 0 deletions src/inversify.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {BuildProvider} from "./view/dataproviders/buildprovider";
import {OsxKeychainApi} from "./bll/credentialsstore/osx/osx-keychain-api";
import {OsxKeychain} from "./bll/credentialsstore/osx/osx-keychain-access";
import {FileTokenStorage} from "./bll/credentialsstore/linux/file-token-storage";
import {WinCredStoreParsingStreamWrapper} from "./bll/credentialsstore/win32/win-credstore-parser";

export const myContainer = new Container();
myContainer.bind<Settings>(TYPES.Settings).to(SettingsImpl).inSingletonScope();
Expand Down Expand Up @@ -76,3 +77,4 @@ myContainer.bind<BuildProvider>(TYPES.BuildProvider).to(BuildProvider).inSinglet
myContainer.bind<OsxKeychainApi>(TYPES.OsxKeychainApi).to(OsxKeychainApi).inSingletonScope();
myContainer.bind<OsxKeychain>(TYPES.OsxKeychain).to(OsxKeychain).inSingletonScope();
myContainer.bind<FileTokenStorage>(TYPES.FileTokenStorage).to(FileTokenStorage).inSingletonScope();
myContainer.bind<WinCredStoreParsingStreamWrapper>(TYPES.WinCredStoreParsingStreamWrapper).to(WinCredStoreParsingStreamWrapper).inSingletonScope();
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import {FileTokenStorage} from "../../src/bll/credentialsstore/linux/file-token-
import * as path from "path";
import {OsxKeychainApi} from "../../src/bll/credentialsstore/osx/osx-keychain-api";
import {OsxKeychain} from "../../src/bll/credentialsstore/osx/osx-keychain-access";
import {WinCredStoreParsingStreamWrapper} from "../../src/bll/credentialsstore/win32/win-credstore-parser";

suite("PersistentStorageManager - integration test", function () {

test("should verify store, get, remove credentials for our add-in. Platform specific", async function () {
try {
const winPersistentCredentialsStore: WinPersistentCredentialsStore = new WinPersistentCredentialsStore();
const winParseWrapper: WinCredStoreParsingStreamWrapper = new WinCredStoreParsingStreamWrapper();
const winPersistentCredentialsStore: WinPersistentCredentialsStore = new WinPersistentCredentialsStore(winParseWrapper);
const fileTokenStorage: FileTokenStorage = new FileTokenStorage();
const defaultFilename: string = "secrets.json";
const defaultFolder: string = ".secrets";
Expand Down

0 comments on commit d1878e7

Please sign in to comment.