Support ASCII-armored and iTunes uploaded SSH key

This commit is contained in:
Bob Sun 2017-04-02 11:21:24 -07:00
parent 894a6cc54c
commit 97d66a8acc
No known key found for this signature in database
GPG key ID: 1F86BA2052FED3B4
11 changed files with 487 additions and 113 deletions

View file

@ -54,6 +54,7 @@
DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */; }; DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC408A31E2FCC9E00F29B0E /* PasswordStore.swift */; };
DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */; }; DCC408C71E307DBB00F29B0E /* SVProgressHUD.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */; };
DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */; }; DCC441521E8F6C06008A90C4 /* RawPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */; };
DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */; };
DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */; }; DCDDEAB01E4639F300F68193 /* LabelTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */; };
DCDDEAB31E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */; }; DCDDEAB31E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */; };
DCE6C2671E71261C003038C6 /* PasswordWithFolderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */; }; DCE6C2671E71261C003038C6 /* PasswordWithFolderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */; };
@ -134,6 +135,7 @@
DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; }; DCC408C61E307DBB00F29B0E /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = "<group>"; };
DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = "<group>"; }; DCC408C91E30BA1300F29B0E /* pass.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = pass.xcdatamodel; sourceTree = "<group>"; };
DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = "<group>"; }; DCC441511E8F6C06008A90C4 /* RawPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawPasswordViewController.swift; sourceTree = "<group>"; };
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitSSHKeyArmorSettingTableViewController.swift; sourceTree = "<group>"; };
DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LabelTableViewCell.xib; sourceTree = "<group>"; }; DCDDEAAF1E4639F300F68193 /* LabelTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LabelTableViewCell.xib; sourceTree = "<group>"; };
DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTitleTableViewCell.swift; sourceTree = "<group>"; }; DCDDEAB11E4896BF00F68193 /* PasswordDetailTitleTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordDetailTitleTableViewCell.swift; sourceTree = "<group>"; };
DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordWithFolderTableViewCell.swift; sourceTree = "<group>"; }; DCE6C2651E71261C003038C6 /* PasswordWithFolderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordWithFolderTableViewCell.swift; sourceTree = "<group>"; };
@ -216,6 +218,7 @@
DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */, DCAAF7441E2FA66800AB94BC /* SettingsTableViewController.swift */,
DC037CA91E4B8EAE00609409 /* SpecialThanksTableViewController.swift */, DC037CA91E4B8EAE00609409 /* SpecialThanksTableViewController.swift */,
DC8963BF1E38EEB900828B09 /* SSHKeySettingTableViewController.swift */, DC8963BF1E38EEB900828B09 /* SSHKeySettingTableViewController.swift */,
DCC441531E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift */,
); );
path = Controllers; path = Controllers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -518,6 +521,7 @@
files = ( files = (
DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */, DCC408A41E2FCC9E00F29B0E /* PasswordStore.swift in Sources */,
DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */, DC037CBF1E4ED4E100609409 /* TextViewTableViewCell.swift in Sources */,
DCC441541E916382008A90C4 /* GitSSHKeyArmorSettingTableViewController.swift in Sources */,
DC8963C01E38EEB900828B09 /* SSHKeySettingTableViewController.swift in Sources */, DC8963C01E38EEB900828B09 /* SSHKeySettingTableViewController.swift in Sources */,
DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */, DC193FFA1E49B4430077E0A3 /* AdvancedSettingsTableViewController.swift in Sources */,
DCFB77AB1E503729008DE471 /* ContentTableViewCell.swift in Sources */, DCFB77AB1E503729008DE471 /* ContentTableViewCell.swift in Sources */,

View file

@ -477,10 +477,11 @@
<connections> <connections>
<outlet property="authPasswordCell" destination="KrP-nb-haa" id="zjH-sg-dfJ"/> <outlet property="authPasswordCell" destination="KrP-nb-haa" id="zjH-sg-dfJ"/>
<outlet property="authSSHKeyCell" destination="Qmt-bo-CuJ" id="11L-Tt-MgM"/> <outlet property="authSSHKeyCell" destination="Qmt-bo-CuJ" id="11L-Tt-MgM"/>
<outlet property="gitRepositoryURLTextField" destination="EVT-VU-sCi" id="2EH-Uq-tbU"/> <outlet property="gitURLTextField" destination="EVT-VU-sCi" id="XdU-3l-Nsv"/>
<outlet property="usernameTextField" destination="TMg-Gk-7nG" id="htL-4C-WJF"/> <outlet property="usernameTextField" destination="TMg-Gk-7nG" id="htL-4C-WJF"/>
<segue destination="7K9-cE-9qq" kind="unwind" identifier="saveGitServerSettingSegue" unwindAction="saveGitServerSettingWithSegue:" id="5UN-sC-xCA"/> <segue destination="7K9-cE-9qq" kind="unwind" identifier="saveGitServerSettingSegue" unwindAction="saveGitServerSettingWithSegue:" id="5UN-sC-xCA"/>
<segue destination="hqC-Ic-NMi" kind="show" identifier="showSSHKeySettingSegue" id="AP7-FV-2Ow"/> <segue destination="hqC-Ic-NMi" kind="show" identifier="setGitSSHKeyByURLSegue" id="AP7-FV-2Ow"/>
<segue destination="WgM-cY-mig" kind="show" identifier="setGitSSHKeyByArmorSegue" id="HLm-0q-hUg"/>
</connections> </connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="7c1-c7-Qyp" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="7c1-c7-Qyp" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -752,7 +753,7 @@
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3dt-Ph-4As"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3dt-Ph-4As">
<rect key="frame" x="20" y="4" width="374" height="732"/> <rect key="frame" x="20" y="4" width="374" height="732"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<mutableString key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</mutableString> <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="14"/> <fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>
@ -892,36 +893,6 @@
</constraints> </constraints>
</tableViewCellContentView> </tableViewCellContentView>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="passphrase" rowHeight="52" id="yER-vT-YTO">
<rect key="frame" x="0.0" y="139" width="414" height="52"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="yER-vT-YTO" id="Mip-zw-xLu">
<rect key="frame" x="0.0" y="0.0" width="414" height="51.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Passphrase" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Rnj-2x-ksO">
<rect key="frame" x="15" y="8" width="391" height="15"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Passphrase" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="4oZ-lc-EFS">
<rect key="frame" x="15" y="23" width="391" height="21"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" secureTextEntry="YES"/>
</textField>
</subviews>
<constraints>
<constraint firstItem="Rnj-2x-ksO" firstAttribute="leading" secondItem="Mip-zw-xLu" secondAttribute="leadingMargin" constant="7" id="B0m-eX-UKn"/>
<constraint firstItem="4oZ-lc-EFS" firstAttribute="top" secondItem="Rnj-2x-ksO" secondAttribute="bottom" id="V7l-fu-KnP"/>
<constraint firstAttribute="trailingMargin" secondItem="Rnj-2x-ksO" secondAttribute="trailing" id="nEm-LM-Uwg"/>
<constraint firstAttribute="topMargin" secondItem="Rnj-2x-ksO" secondAttribute="top" id="nPH-l3-bRA"/>
<constraint firstAttribute="trailingMargin" secondItem="4oZ-lc-EFS" secondAttribute="trailing" id="uIe-w7-hcX"/>
<constraint firstItem="4oZ-lc-EFS" firstAttribute="leading" secondItem="Rnj-2x-ksO" secondAttribute="leading" id="vEc-EI-F0z"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells> </cells>
</tableViewSection> </tableViewSection>
</sections> </sections>
@ -931,14 +902,13 @@
</connections> </connections>
</tableView> </tableView>
<connections> <connections>
<outlet property="passphraseTextField" destination="4oZ-lc-EFS" id="dtf-H3-gBh"/>
<outlet property="privateKeyURLTextField" destination="4iJ-oB-R1f" id="scx-lz-dUW"/> <outlet property="privateKeyURLTextField" destination="4iJ-oB-R1f" id="scx-lz-dUW"/>
<outlet property="publicKeyURLTextField" destination="Q1j-Z4-Ae8" id="nfA-W3-kJ2"/> <outlet property="publicKeyURLTextField" destination="Q1j-Z4-Ae8" id="nfA-W3-kJ2"/>
</connections> </connections>
</tableViewController> </tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="eY3-aM-BJB" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="eY3-aM-BJB" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="5132" y="2893"/> <point key="canvasLocation" x="7433" y="2204"/>
</scene> </scene>
<!--General Settings Table View Controller--> <!--General Settings Table View Controller-->
<scene sceneID="fho-xr-1Ah"> <scene sceneID="fho-xr-1Ah">
@ -1371,7 +1341,7 @@ pfZ36xQbOAQYKKf6ZTT5R/Y=
<placeholder placeholderIdentifier="IBFirstResponder" id="2Wn-7D-LLj" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="2Wn-7D-LLj" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="Ul9-vk-jhw" userLabel="Exit" sceneMemberID="exit"/> <exit id="Ul9-vk-jhw" userLabel="Exit" sceneMemberID="exit"/>
</objects> </objects>
<point key="canvasLocation" x="6039.130434782609" y="4307.608695652174"/> <point key="canvasLocation" x="7094" y="4030"/>
</scene> </scene>
<!--Navigation Controller--> <!--Navigation Controller-->
<scene sceneID="m18-ch-m92"> <scene sceneID="m18-ch-m92">
@ -1409,6 +1379,200 @@ pfZ36xQbOAQYKKf6ZTT5R/Y=
</objects> </objects>
<point key="canvasLocation" x="6720" y="-24"/> <point key="canvasLocation" x="6720" y="-24"/>
</scene> </scene>
<!--GitSSH Key Armor Setting Table View Controller-->
<scene sceneID="zWR-BT-dXv">
<objects>
<tableViewController id="WgM-cY-mig" customClass="GitSSHKeyArmorSettingTableViewController" customModule="pass" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="e2Q-qC-LUk">
<rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="HgL-ju-8xi">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="170" id="EMz-nP-2Om">
<rect key="frame" x="0.0" y="35" width="414" height="170"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EMz-nP-2Om" id="XW5-mL-PRC">
<rect key="frame" x="0.0" y="0.0" width="414" height="169.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" delaysContentTouches="NO" canCancelContentTouches="NO" bouncesZoom="NO" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vkU-ka-wAm">
<rect key="frame" x="15" y="22" width="391" height="146.66666666666666"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<attributedString key="attributedText">
<fragment>
<string key="content">ASCII-armored key format similar to uuencoded documents rather than binary format.
</string>
<attributes>
<color key="NSColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="14" name=".AppleSystemUIFont"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
<fragment>
<string key="content"> $ cat ~/.ssh/id_rsa.pub
$ cat ~/.ssh/id_rsa</string>
<attributes>
<color key="NSColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<font key="NSFont" size="13" name="Menlo-Regular"/>
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural" tighteningFactorForTruncation="0.0"/>
</attributes>
</fragment>
</attributedString>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="ASCII-Armor Keys" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HIF-Zn-aBt">
<rect key="frame" x="15" y="8" width="391" height="21"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="Sh8-Aj-s7i"/>
</constraints>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="14"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="HIF-Zn-aBt" firstAttribute="leading" secondItem="XW5-mL-PRC" secondAttribute="leadingMargin" constant="7" id="2fZ-Tu-6jx"/>
<constraint firstAttribute="trailingMargin" secondItem="vkU-ka-wAm" secondAttribute="trailing" id="I1w-xW-439"/>
<constraint firstItem="vkU-ka-wAm" firstAttribute="top" secondItem="HIF-Zn-aBt" secondAttribute="bottom" constant="-7" id="QEU-iz-4AS"/>
<constraint firstItem="vkU-ka-wAm" firstAttribute="leading" secondItem="XW5-mL-PRC" secondAttribute="leadingMargin" constant="7" id="UEQ-vW-A0q"/>
<constraint firstAttribute="trailingMargin" secondItem="HIF-Zn-aBt" secondAttribute="trailing" id="h7g-Ft-YVp"/>
<constraint firstAttribute="bottomMargin" secondItem="vkU-ka-wAm" secondAttribute="bottom" constant="-7" id="lHv-ws-fct"/>
<constraint firstAttribute="topMargin" secondItem="HIF-Zn-aBt" secondAttribute="top" id="uqY-0s-I2H"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="ASCII-Armor Encrypted Public Key" id="w4D-Mh-0pP">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="160" id="3po-uS-Nch">
<rect key="frame" x="0.0" y="261" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3po-uS-Nch" id="pet-if-EHU">
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Y9t-DD-6uH">
<rect key="frame" x="15" y="8" width="391" height="143.66666666666666"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<string key="text">-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFiiqFoBCACzvPOW+J1RlQOI9oB7Iwuk92rzpkKGC8tiwo4otHMc3rxmzYlG
gEdHmSi6Ix0+hYZ2vSWJamPQl9WUiYSBQmkVwoqiwXNWcqRePOylcqsj58owIxZt
23nXUIG78t+5BatoWyl9/qXsMUMSink7RAlOnjCyvoSJw9yzUVeWn723rvAr7ypV
yqjce4gOGIfGT8PpGXThDQjvg4gM+wxUD3+oNhW50Qb+crMsefxwEoGXbCA4+6ZO
nJGy2MhRr4FkXwNAAcvTvTmYXQfH8Q8xCtgl4PyxQA4OAeoEfxl5FOuOGIbNKgMs
133n2oue5dy3YERN2t9/Di8Iov4Bsc+nj6+vABEBAAG0E1Bhc3MgZm9yIGlPUyAo
RGVtbymJATcEEwEKACEFAliiqFoCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AA
CgkQl/oT92AAdt0TqAgAlTsGLTe9+qY9pfyBVpDk+z1SC9kaOYsvoj7hSWVOXxwP
yn4jxjHUr1C9fyaZqP9K4yIdl9jsGBkpaR6lhnUm9x5RO5gEAAAaLhx3ZgE7/XQg
y7WHibxsnLaTEMVnNh8BJzQveYSqO8frBkQofj0K9LNiqWHhcllKKNkVHqyeMRII
KYPOXvDnsnKqywapu5VyyErMnPqq6h7i3Z7+xDfVk8iUew9i0LR/QEmJO7iTH98C
DczQo+4VnflBIHiNcMaXQqC2Li5rS+O3a9hq5V/gJwBtIx1iXvDaRATH4aUANxTZ
POHVLwxBuTwilhnEaAPGkGu+oG1XF+RWOyrGYluWc7kBDQRYoqhaAQgAoh9B+sY1
jZZgnqP11a9wkjom2DcBJLreYNzgBFYhUcmxtq+ZWyorPFzMJpbIjzMGRiGWFIus
DE5D5LZdAvkINWKsbVN6Y3OI6Tj+/O45SuJ7EusG8Z1bwmjJqHy/Y//Fk0rF6ZFs
g0+p12Xzr5k8sMKUU4I5T4DNrsS+mwnOPItC7NGawkArPD7Mew0ma+Luv7b3g0gA
DuofvxKiN0jq6W9QPUNRY4BKm+Y03CdZNXyMDs/NTguDdfarawTIAIlJOSg5rV7S
74v73dtZkB5LySX1rBomRb5zkCg6/Ygap4RO1aO9gbXyPmc2lUQSBAJCS9d8h0qD
sNGembqhGDwHpQARAQABiQEfBBgBCgAJBQJYoqhaAhsMAAoJEJf6E/dgAHbdKl4H
/A/Q+hOh40M2B6Sh/Rsus+k1VL51+UXVJRyiNiqvofamufF5ULfeq3Pt5HfqpCsF
/A7iRP0MuanKcZbLDjDv6wWDuRvW9Kwn5qjbb6iBPh+wfyReqNMWGksJTyap0SNR
+Mn+sjbxGkUPjzVdRdqtw64R3DLW6SpMNIUVC6paazCViEtHsSauWwP/0+DRA/6j
xBHSAr0UaLsRTHleOSWV6ABrh9skJRWLBcOoi5pxotBs/3BmjqERQf9bwmmyxj2K
cE60Muv6qY4/KCQqPKhyFjJwXhS/e7BwxHEt1SbUb1aC4lH5UrbW9KYLu5lHJRdY
pfZ36xQbOAQYKKf6ZTT5R/Y=
=2pJ8
-----END PGP PUBLIC KEY BLOCK-----</string>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="8.5"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="Y9t-DD-6uH" secondAttribute="bottom" id="EAK-Wn-RP8"/>
<constraint firstItem="Y9t-DD-6uH" firstAttribute="top" secondItem="pet-if-EHU" secondAttribute="topMargin" id="LW7-SZ-CP3"/>
<constraint firstItem="Y9t-DD-6uH" firstAttribute="leading" secondItem="pet-if-EHU" secondAttribute="leadingMargin" constant="7" id="bw3-EI-8oh"/>
<constraint firstAttribute="trailingMargin" secondItem="Y9t-DD-6uH" secondAttribute="trailing" id="fCY-XN-Wtz"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="ASCII-ARMOR ENCRYPTED PRIVATE KEY" id="sxk-Yb-Y3x">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="pgpKeyURLTableViewCell" rowHeight="160" id="nmc-vy-Ab5">
<rect key="frame" x="0.0" y="477" width="414" height="160"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nmc-vy-Ab5" id="TQD-GC-YOY">
<rect key="frame" x="0.0" y="0.0" width="414" height="159.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="23o-MP-wQY">
<rect key="frame" x="15" y="8" width="391" height="144"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<string key="text">-----BEGIN PGP PUBLIC KEY BLOCK-----
mQENBFiiqFoBCACzvPOW+J1RlQOI9oB7Iwuk92rzpkKGC8tiwo4otHMc3rxmzYlG
gEdHmSi6Ix0+hYZ2vSWJamPQl9WUiYSBQmkVwoqiwXNWcqRePOylcqsj58owIxZt
23nXUIG78t+5BatoWyl9/qXsMUMSink7RAlOnjCyvoSJw9yzUVeWn723rvAr7ypV
yqjce4gOGIfGT8PpGXThDQjvg4gM+wxUD3+oNhW50Qb+crMsefxwEoGXbCA4+6ZO
nJGy2MhRr4FkXwNAAcvTvTmYXQfH8Q8xCtgl4PyxQA4OAeoEfxl5FOuOGIbNKgMs
133n2oue5dy3YERN2t9/Di8Iov4Bsc+nj6+vABEBAAG0E1Bhc3MgZm9yIGlPUyAo
RGVtbymJATcEEwEKACEFAliiqFoCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AA
CgkQl/oT92AAdt0TqAgAlTsGLTe9+qY9pfyBVpDk+z1SC9kaOYsvoj7hSWVOXxwP
yn4jxjHUr1C9fyaZqP9K4yIdl9jsGBkpaR6lhnUm9x5RO5gEAAAaLhx3ZgE7/XQg
y7WHibxsnLaTEMVnNh8BJzQveYSqO8frBkQofj0K9LNiqWHhcllKKNkVHqyeMRII
KYPOXvDnsnKqywapu5VyyErMnPqq6h7i3Z7+xDfVk8iUew9i0LR/QEmJO7iTH98C
DczQo+4VnflBIHiNcMaXQqC2Li5rS+O3a9hq5V/gJwBtIx1iXvDaRATH4aUANxTZ
POHVLwxBuTwilhnEaAPGkGu+oG1XF+RWOyrGYluWc7kBDQRYoqhaAQgAoh9B+sY1
jZZgnqP11a9wkjom2DcBJLreYNzgBFYhUcmxtq+ZWyorPFzMJpbIjzMGRiGWFIus
DE5D5LZdAvkINWKsbVN6Y3OI6Tj+/O45SuJ7EusG8Z1bwmjJqHy/Y//Fk0rF6ZFs
g0+p12Xzr5k8sMKUU4I5T4DNrsS+mwnOPItC7NGawkArPD7Mew0ma+Luv7b3g0gA
DuofvxKiN0jq6W9QPUNRY4BKm+Y03CdZNXyMDs/NTguDdfarawTIAIlJOSg5rV7S
74v73dtZkB5LySX1rBomRb5zkCg6/Ygap4RO1aO9gbXyPmc2lUQSBAJCS9d8h0qD
sNGembqhGDwHpQARAQABiQEfBBgBCgAJBQJYoqhaAhsMAAoJEJf6E/dgAHbdKl4H
/A/Q+hOh40M2B6Sh/Rsus+k1VL51+UXVJRyiNiqvofamufF5ULfeq3Pt5HfqpCsF
/A7iRP0MuanKcZbLDjDv6wWDuRvW9Kwn5qjbb6iBPh+wfyReqNMWGksJTyap0SNR
+Mn+sjbxGkUPjzVdRdqtw64R3DLW6SpMNIUVC6paazCViEtHsSauWwP/0+DRA/6j
xBHSAr0UaLsRTHleOSWV6ABrh9skJRWLBcOoi5pxotBs/3BmjqERQf9bwmmyxj2K
cE60Muv6qY4/KCQqPKhyFjJwXhS/e7BwxHEt1SbUb1aC4lH5UrbW9KYLu5lHJRdY
pfZ36xQbOAQYKKf6ZTT5R/Y=
=2pJ8
-----END PGP PUBLIC KEY BLOCK-----</string>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="8.5"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="bottomMargin" secondItem="23o-MP-wQY" secondAttribute="bottom" id="45J-dA-N4r"/>
<constraint firstItem="23o-MP-wQY" firstAttribute="top" secondItem="TQD-GC-YOY" secondAttribute="topMargin" id="Tth-YH-Ni9"/>
<constraint firstAttribute="trailingMargin" secondItem="23o-MP-wQY" secondAttribute="trailing" id="lqq-rV-fZK"/>
<constraint firstItem="23o-MP-wQY" firstAttribute="leading" secondItem="TQD-GC-YOY" secondAttribute="leadingMargin" constant="7" id="oYc-y1-XS9"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="WgM-cY-mig" id="7bV-Gr-WNu"/>
<outlet property="delegate" destination="WgM-cY-mig" id="lsz-m8-HZb"/>
</connections>
</tableView>
<connections>
<outlet property="armorPrivateKeyTextView" destination="23o-MP-wQY" id="ybK-Ba-vJt"/>
<outlet property="armorPublicKeyTextView" destination="Y9t-DD-6uH" id="307-xN-QZ9"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="qJO-AN-K9p" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="6870" y="3117"/>
</scene>
</scenes> </scenes>
<resources> <resources>
<image name="Lock" width="25" height="25"/> <image name="Lock" width="25" height="25"/>

View file

@ -0,0 +1,72 @@
//
// GitSSHKeyArmorSettingTableViewController.swift
// pass
//
// Created by Mingshen Sun on 2/4/2017.
// Copyright © 2017 Bob Sun. All rights reserved.
//
import UIKit
import SwiftyUserDefaults
class GitSSHKeyArmorSettingTableViewController: UITableViewController {
@IBOutlet weak var armorPublicKeyTextView: UITextView!
@IBOutlet weak var armorPrivateKeyTextView: UITextView!
var gitSSHPrivateKeyPassphrase: String?
let passwordStore = PasswordStore.shared
var doneBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
armorPublicKeyTextView.text = Defaults[.gitSSHPublicKeyArmor]
armorPrivateKeyTextView.text = Defaults[.gitSSHPrivateKeyArmor]
gitSSHPrivateKeyPassphrase = passwordStore.gitSSHPrivateKeyPassphrase
doneBarButtonItem = UIBarButtonItem(title: "Done",
style: UIBarButtonItemStyle.done,
target: self,
action: #selector(doneButtonTapped(_:)))
navigationItem.rightBarButtonItem = doneBarButtonItem
navigationItem.title = "SSH Key"
}
private func createSavePassphraseAlert() -> UIAlertController {
let savePassphraseAlert = UIAlertController(title: "Passphrase", message: "Do you want to save the passphrase for later sync?", preferredStyle: UIAlertControllerStyle.alert)
savePassphraseAlert.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.default) { _ in
Defaults[.isRememberPassphraseOn] = false
Defaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true)
})
savePassphraseAlert.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.destructive) {_ in
Defaults[.isRememberPassphraseOn] = true
self.passwordStore.gitSSHPrivateKeyPassphrase = self.gitSSHPrivateKeyPassphrase
Defaults[.gitSSHKeySource] = "armor"
self.navigationController!.popViewController(animated: true)
})
return savePassphraseAlert
}
func doneButtonTapped(_ sender: Any) {
Defaults[.gitSSHPublicKeyArmor] = armorPublicKeyTextView.text
Defaults[.gitSSHPrivateKeyArmor] = armorPrivateKeyTextView.text
do {
try passwordStore.initGitSSHKey(with: armorPublicKeyTextView.text, .public)
try passwordStore.initGitSSHKey(with: armorPrivateKeyTextView.text, .secret)
} catch {
Utils.alert(title: "Cannot Save", message: "Cannot Save SSH Key", controller: self, completion: nil)
}
let alert = UIAlertController(title: "Passphrase", message: "Please fill in the passphrase of your SSH secret key.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
self.gitSSHPrivateKeyPassphrase = alert.textFields?.first?.text
let savePassphraseAlert = self.createSavePassphraseAlert()
self.present(savePassphraseAlert, animated: true, completion: nil)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
}
}

View file

@ -8,17 +8,18 @@
import UIKit import UIKit
import SwiftyUserDefaults import SwiftyUserDefaults
import SVProgressHUD
class GitServerSettingTableViewController: UITableViewController { class GitServerSettingTableViewController: UITableViewController {
@IBOutlet weak var gitRepositoryURLTextField: UITextField! @IBOutlet weak var gitURLTextField: UITextField!
@IBOutlet weak var usernameTextField: UITextField! @IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var authSSHKeyCell: UITableViewCell! @IBOutlet weak var authSSHKeyCell: UITableViewCell!
@IBOutlet weak var authPasswordCell: UITableViewCell! @IBOutlet weak var authPasswordCell: UITableViewCell!
let passwordStore = PasswordStore.shared let passwordStore = PasswordStore.shared
var password: String? var password: String?
var authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod] var authenticationMethod = Defaults[.gitAuthenticationMethod]
private func checkAuthenticationMethod(method: String) { private func checkAuthenticationMethod(method: String) {
let passwordCheckView = authPasswordCell.viewWithTag(1001)! let passwordCheckView = authPasswordCell.viewWithTag(1001)!
@ -38,17 +39,17 @@ class GitServerSettingTableViewController: UITableViewController {
} }
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
if let url = Defaults[.gitRepositoryURL] { if let url = Defaults[.gitURL] {
gitRepositoryURLTextField.text = url.absoluteString gitURLTextField.text = url.absoluteString
} }
usernameTextField.text = Defaults[.gitRepositoryUsername] usernameTextField.text = Defaults[.gitUsername]
password = passwordStore.gitRepositoryPassword password = passwordStore.gitPassword
authenticationMethod = Defaults[.gitRepositoryAuthenticationMethod] authenticationMethod = Defaults[.gitAuthenticationMethod]
// Grey out ssh option if ssh_key and ssh_key.pub are not present // Grey out ssh option if ssh_key and ssh_key.pub are not present
let sshLabel = authSSHKeyCell.subviews[0].subviews[0] as! UILabel let sshLabel = authSSHKeyCell.subviews[0].subviews[0] as! UILabel
sshLabel.isEnabled = sshKeyExists() sshLabel.isEnabled = gitSSHKeyExists()
if authenticationMethod == nil || !sshLabel.isEnabled { if authenticationMethod == nil || !sshLabel.isEnabled {
authenticationMethod = "Password" authenticationMethod = "Password"
@ -61,7 +62,7 @@ class GitServerSettingTableViewController: UITableViewController {
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) let cell = tableView.cellForRow(at: indexPath)
if cell == authSSHKeyCell { if cell == authSSHKeyCell {
performSegue(withIdentifier: "showSSHKeySettingSegue", sender: self) showSSHKeyActionSheet()
} }
} }
@ -77,7 +78,7 @@ class GitServerSettingTableViewController: UITableViewController {
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool { override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
if identifier == "saveGitServerSettingSegue" { if identifier == "saveGitServerSettingSegue" {
guard let _ = URL(string: gitRepositoryURLTextField.text!) else { guard let _ = URL(string: gitURLTextField.text!) else {
Utils.alert(title: "Cannot Save", message: "Git Server is not set.", controller: self, completion: nil) Utils.alert(title: "Cannot Save", message: "Git Server is not set.", controller: self, completion: nil)
return false return false
} }
@ -89,18 +90,13 @@ class GitServerSettingTableViewController: UITableViewController {
return true return true
} }
func sshKeyExists() -> Bool {
return FileManager.default.fileExists(atPath: Globals.sshPublicKeyURL.path) &&
FileManager.default.fileExists(atPath: Globals.sshPrivateKeyURL.path)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) let cell = tableView.cellForRow(at: indexPath)
if cell == authPasswordCell { if cell == authPasswordCell {
authenticationMethod = "Password" authenticationMethod = "Password"
} else if cell == authSSHKeyCell { } else if cell == authSSHKeyCell {
if !sshKeyExists() { if !gitSSHKeyExists() {
Utils.alert(title: "Cannot Select SSH Key", message: "Please setup SSH key first.", controller: self, completion: nil) Utils.alert(title: "Cannot Select SSH Key", message: "Please setup SSH key first.", controller: self, completion: nil)
authenticationMethod = "Password" authenticationMethod = "Password"
} else { } else {
@ -131,4 +127,74 @@ class GitServerSettingTableViewController: UITableViewController {
} }
} }
} }
private func gitSSHKeyExists() -> Bool {
return FileManager.default.fileExists(atPath: Globals.gitSSHPublicKeyPath) &&
FileManager.default.fileExists(atPath: Globals.gitSSHPrivateKeyPath)
}
func showSSHKeyActionSheet() {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
var urlActionTitle = "Download from URL"
var armorActionTitle = "ASCII-Armor Encrypted Key"
var fileActionTitle = "Use Uploaded Keys"
if Defaults[.gitSSHKeySource] == "url" {
urlActionTitle = "\(urlActionTitle)"
} else if Defaults[.gitSSHKeySource] == "armor" {
armorActionTitle = "\(armorActionTitle)"
} else if Defaults[.gitSSHKeySource] == "file" {
fileActionTitle = "\(fileActionTitle)"
}
let urlAction = UIAlertAction(title: urlActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByURLSegue", sender: self)
}
let armorAction = UIAlertAction(title: armorActionTitle, style: .default) { _ in
self.performSegue(withIdentifier: "setGitSSHKeyByArmorSegue", sender: self)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
optionMenu.addAction(urlAction)
optionMenu.addAction(armorAction)
if (gitSSHKeyExists()) {
let fileAction = UIAlertAction(title: fileActionTitle, style: .default) { _ in
let alert = UIAlertController(
title: "SSH Key Passphrase",
message: "Please fill in the passphrase for your Git Repository SSH key.",
preferredStyle: UIAlertControllerStyle.alert
)
alert.addAction(
UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: {_ in
self.passwordStore.gitSSHPrivateKeyPassphrase = alert.textFields!.first!.text!
}
)
)
alert.addTextField(
configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
}
)
}
Defaults[.gitSSHKeySource] = "file"
optionMenu.addAction(fileAction)
}
if Defaults[.gitSSHKeySource] != nil {
let deleteAction = UIAlertAction(title: "Remove Git SSH Keys", style: .destructive) { _ in
Utils.removeGitSSHKeys()
Defaults[.gitSSHKeySource] = nil
}
optionMenu.addAction(deleteAction)
}
optionMenu.addAction(cancelAction)
optionMenu.popoverPresentationController?.sourceView = authSSHKeyCell
optionMenu.popoverPresentationController?.sourceRect = authSSHKeyCell.bounds
self.present(optionMenu, animated: true, completion: nil)
}
} }

View file

@ -143,7 +143,7 @@ class PasswordsViewController: UIViewController, UITableViewDataSource, UITableV
} }
DispatchQueue.main.async { DispatchQueue.main.async {
self.reloadTableView(parent: nil) self.reloadTableView(parent: nil)
Defaults[.gitRepositoryPasswordAttempts] = 0 Defaults[.gitPasswordAttempts] = 0
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }

View file

@ -12,15 +12,14 @@ import SVProgressHUD
class SSHKeySettingTableViewController: UITableViewController { class SSHKeySettingTableViewController: UITableViewController {
@IBOutlet weak var passphraseTextField: UITextField!
@IBOutlet weak var privateKeyURLTextField: UITextField! @IBOutlet weak var privateKeyURLTextField: UITextField!
@IBOutlet weak var publicKeyURLTextField: UITextField! @IBOutlet weak var publicKeyURLTextField: UITextField!
let passwordStore = PasswordStore.shared
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
passphraseTextField.text = Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? "" privateKeyURLTextField.text = Defaults[.gitSSHPrivateKeyURL]?.absoluteString
privateKeyURLTextField.text = Defaults[.gitRepositorySSHPrivateKeyURL]?.absoluteString publicKeyURLTextField.text = Defaults[.gitSSHPublicKeyURL]?.absoluteString
publicKeyURLTextField.text = Defaults[.gitRepositorySSHPublicKeyURL]?.absoluteString
var doneBarButtonItem: UIBarButtonItem? var doneBarButtonItem: UIBarButtonItem?
doneBarButtonItem = UIBarButtonItem(title: "Done", doneBarButtonItem = UIBarButtonItem(title: "Done",
@ -41,18 +40,42 @@ class SSHKeySettingTableViewController: UITableViewController {
return return
} }
Defaults[.gitRepositorySSHPublicKeyURL] = publicKeyURL Defaults[.gitSSHPublicKeyURL] = publicKeyURL
Defaults[.gitRepositorySSHPrivateKeyURL] = privateKeyURL Defaults[.gitSSHPrivateKeyURL] = privateKeyURL
Utils.addPasswordToKeychain(name: "gitRepositorySSHPrivateKeyPassphrase", password: passphraseTextField.text!)
do { do {
try Data(contentsOf: publicKeyURL).write(to: Globals.sshPublicKeyURL, options: .atomic) try Data(contentsOf: publicKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPublicKeyPath), options: .atomic)
try Data(contentsOf: privateKeyURL).write(to: Globals.sshPrivateKeyURL, options: .atomic) try Data(contentsOf: privateKeyURL).write(to: URL(fileURLWithPath: Globals.gitSSHPrivateKeyPath), options: .atomic)
} catch { } catch {
Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil) Utils.alert(title: "Error", message: error.localizedDescription, controller: self, completion: nil)
} }
Defaults[.gitSSHKeySource] = "url"
let alert = UIAlertController(
title: "PGP Passphrase",
message: "Please fill in the passphrase for your Git Repository SSH key.",
preferredStyle: UIAlertControllerStyle.alert
)
navigationController!.popViewController(animated: true) alert.addAction(
UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: {_ in
Utils.addPasswordToKeychain(
name: "gitSSHPrivateKeyPassphrase",
password: alert.textFields!.first!.text!
)
self.navigationController!.popViewController(animated: true)
}
)
)
alert.addTextField(
configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitSSHPrivateKeyPassphrase
textField.isSecureTextEntry = true
})
self.present(alert, animated: true, completion: nil)
} }
} }

View file

@ -95,16 +95,16 @@ class SettingsTableViewController: UITableViewController {
@IBAction func saveGitServerSetting(segue: UIStoryboardSegue) { @IBAction func saveGitServerSetting(segue: UIStoryboardSegue) {
if let controller = segue.source as? GitServerSettingTableViewController { if let controller = segue.source as? GitServerSettingTableViewController {
let gitRepostiroyURL = controller.gitRepositoryURLTextField.text! let gitRepostiroyURL = controller.gitURLTextField.text!
let username = controller.usernameTextField.text! let username = controller.usernameTextField.text!
let password = controller.password let password = controller.password
let auth = controller.authenticationMethod let auth = controller.authenticationMethod
if Defaults[.gitRepositoryURL] == nil || if Defaults[.gitURL] == nil ||
Defaults[.gitRepositoryURL]!.absoluteString != gitRepostiroyURL || Defaults[.gitURL]!.absoluteString != gitRepostiroyURL ||
auth != Defaults[.gitRepositoryAuthenticationMethod] || auth != Defaults[.gitAuthenticationMethod] ||
username != Defaults[.gitRepositoryUsername] || username != Defaults[.gitUsername] ||
password != self.passwordStore.gitRepositoryPassword || password != self.passwordStore.gitPassword ||
self.passwordStore.repositoryExisted() == false { self.passwordStore.repositoryExisted() == false {
SVProgressHUD.setDefaultMaskType(.black) SVProgressHUD.setDefaultMaskType(.black)
@ -117,9 +117,9 @@ class SettingsTableViewController: UITableViewController {
gitCredential = GitCredential( gitCredential = GitCredential(
credential: GitCredential.Credential.ssh( credential: GitCredential.Credential.ssh(
userName: username, userName: username,
password: Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? "", password: Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? "",
publicKeyFile: Globals.sshPublicKeyURL, publicKeyFile: Globals.gitSSHPublicKeyURL,
privateKeyFile: Globals.sshPrivateKeyURL, privateKeyFile: Globals.gitSSHPrivateKeyURL,
passwordNotSetCallback: self.requestSshKeyPassword passwordNotSetCallback: self.requestSshKeyPassword
) )
) )
@ -140,11 +140,11 @@ class SettingsTableViewController: UITableViewController {
} }
}) })
DispatchQueue.main.async { DispatchQueue.main.async {
Defaults[.gitRepositoryURL] = URL(string: gitRepostiroyURL) Defaults[.gitURL] = URL(string: gitRepostiroyURL)
Defaults[.gitRepositoryUsername] = username Defaults[.gitUsername] = username
Defaults[.gitRepositoryAuthenticationMethod] = auth Defaults[.gitAuthenticationMethod] = auth
Defaults[.gitRepositoryPasswordAttempts] = 0 Defaults[.gitPasswordAttempts] = 0
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]?.host self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
SVProgressHUD.showSuccess(withStatus: "Done") SVProgressHUD.showSuccess(withStatus: "Done")
SVProgressHUD.dismiss(withDelay: 1) SVProgressHUD.dismiss(withDelay: 1)
} }
@ -162,7 +162,7 @@ class SettingsTableViewController: UITableViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(SettingsTableViewController.actOnPasswordStoreErasedNotification), name: .passwordStoreErased, object: nil)
self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]?.host self.passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]?.host
touchIDTableViewCell.accessoryView = touchIDSwitch touchIDTableViewCell.accessoryView = touchIDSwitch
setPGPKeyTableViewCellDetailText() setPGPKeyTableViewCellDetailText()
setPasswordRepositoryTableViewCellDetailText() setPasswordRepositoryTableViewCellDetailText()
@ -189,10 +189,10 @@ class SettingsTableViewController: UITableViewController {
} }
private func setPasswordRepositoryTableViewCellDetailText() { private func setPasswordRepositoryTableViewCellDetailText() {
if Defaults[.gitRepositoryURL] == nil { if Defaults[.gitURL] == nil {
passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set" passwordRepositoryTableViewCell.detailTextLabel?.text = "Not Set"
} else { } else {
passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitRepositoryURL]!.host passwordRepositoryTableViewCell.detailTextLabel?.text = Defaults[.gitURL]!.host
} }
} }
@ -218,7 +218,7 @@ class SettingsTableViewController: UITableViewController {
})) }))
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = self.passwordStore.gitRepositoryPassword textField.text = self.passwordStore.gitPassword
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
}) })

View file

@ -17,12 +17,17 @@ extension DefaultsKeys {
static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor") static let pgpPublicKeyArmor = DefaultsKey<String?>("pgpPublicKeyArmor")
static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor") static let pgpPrivateKeyArmor = DefaultsKey<String?>("pgpPrivateKeyArmor")
static let gitRepositoryURL = DefaultsKey<URL?>("gitRepositoryURL") static let gitURL = DefaultsKey<URL?>("gitURL")
static let gitRepositoryAuthenticationMethod = DefaultsKey<String?>("gitRepositoryAuthenticationMethod") static let gitAuthenticationMethod = DefaultsKey<String?>("gitAuthenticationMethod")
static let gitRepositoryUsername = DefaultsKey<String?>("gitRepositoryUsername") static let gitUsername = DefaultsKey<String?>("gitUsername")
static let gitRepositoryPasswordAttempts = DefaultsKey<Int>("gitRepositoryPasswordAttempts") static let gitPasswordAttempts = DefaultsKey<Int>("gitPasswordAttempts")
static let gitRepositorySSHPublicKeyURL = DefaultsKey<URL?>("gitRepositorySSHPublicKeyURL") static let gitSSHPublicKeyURL = DefaultsKey<URL?>("gitSSHPublicKeyURL")
static let gitRepositorySSHPrivateKeyURL = DefaultsKey<URL?>("gitRepositorySSHPrivateKeyURL") static let gitSSHPrivateKeyURL = DefaultsKey<URL?>("gitSSHPrivateKeyURL")
static let gitSSHKeySource = DefaultsKey<String?>("gitSSHKeySource")
static let gitSSHPublicKeyArmor = DefaultsKey<String?>("gitSSHPublicKeyArmor")
static let gitSSHPrivateKeyArmor = DefaultsKey<String?>("gitSSHPrivateKeyArmor")
static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime") static let lastSyncedTime = DefaultsKey<Date?>("lastSyncedTime")

View file

@ -15,8 +15,10 @@ class Globals {
static let pgpPublicKeyPath = "\(documentPath)/gpg_key.pub" static let pgpPublicKeyPath = "\(documentPath)/gpg_key.pub"
static let pgpPrivateKeyPath = "\(documentPath)/gpg_key" static let pgpPrivateKeyPath = "\(documentPath)/gpg_key"
static let sshPublicKeyURL = URL(fileURLWithPath: "\(documentPath)/ssh_key.pub") static let gitSSHPublicKeyPath = "\(documentPath)/ssh_key.pub"
static let sshPrivateKeyURL = URL(fileURLWithPath: "\(documentPath)/ssh_key") static let gitSSHPrivateKeyPath = "\(documentPath)/ssh_key"
static let gitSSHPublicKeyURL = URL(fileURLWithPath: gitSSHPublicKeyPath)
static let gitSSHPrivateKeyURL = URL(fileURLWithPath: gitSSHPrivateKeyPath)
static let repositoryPath = "\(libraryPath)/password-store" static let repositoryPath = "\(libraryPath)/password-store"
static var passcodeConfiguration = PasscodeLockConfiguration() static var passcodeConfiguration = PasscodeLockConfiguration()

View file

@ -83,6 +83,16 @@ class Utils {
Utils.removeKeychain(name: ".pgpKeyPassphrase") Utils.removeKeychain(name: ".pgpKeyPassphrase")
} }
static func removeGitSSHKeys() {
removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath)
removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath)
Defaults.remove(.gitSSHPublicKeyArmor)
Defaults.remove(.gitSSHPrivateKeyArmor)
Defaults.remove(.gitSSHPublicKeyURL)
Defaults.remove(.gitSSHPrivateKeyURL)
Utils.removeKeychain(name: ".gitSSHPrivateKeyPassphrase")
}
static func getPasswordFromKeychain(name: String) -> String? { static func getPasswordFromKeychain(name: String) -> String? {
let keychain = Keychain(service: "me.mssun.passforios") let keychain = Keychain(service: "me.mssun.passforios")
do { do {

View file

@ -27,9 +27,9 @@ struct GitCredential {
var credential: GTCredential? = nil var credential: GTCredential? = nil
switch self.credential { switch self.credential {
case let .http(userName, password): case let .http(userName, password):
print(Defaults[.gitRepositoryPasswordAttempts]) print(Defaults[.gitPasswordAttempts])
var newPassword: String = password var newPassword: String = password
if Defaults[.gitRepositoryPasswordAttempts] != 0 { if Defaults[.gitPasswordAttempts] != 0 {
let sem = DispatchSemaphore(value: 0) let sem = DispatchSemaphore(value: 0)
DispatchQueue.main.async { DispatchQueue.main.async {
SVProgressHUD.dismiss() SVProgressHUD.dismiss()
@ -40,15 +40,15 @@ struct GitCredential {
let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert) let alert = UIAlertController(title: "Password", message: "Please fill in the password of your Git account.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {_ in
newPassword = alert.textFields!.first!.text! newPassword = alert.textFields!.first!.text!
PasswordStore.shared.gitRepositoryPassword = newPassword PasswordStore.shared.gitPassword = newPassword
sem.signal() sem.signal()
})) }))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
Defaults[.gitRepositoryPasswordAttempts] = -1 Defaults[.gitPasswordAttempts] = -1
sem.signal() sem.signal()
}) })
alert.addTextField(configurationHandler: {(textField: UITextField!) in alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.text = PasswordStore.shared.gitRepositoryPassword textField.text = PasswordStore.shared.gitPassword
textField.isSecureTextEntry = true textField.isSecureTextEntry = true
}) })
topController.present(alert, animated: true, completion: nil) topController.present(alert, animated: true, completion: nil)
@ -56,12 +56,12 @@ struct GitCredential {
} }
let _ = sem.wait(timeout: DispatchTime.distantFuture) let _ = sem.wait(timeout: DispatchTime.distantFuture)
} }
if Defaults[.gitRepositoryPasswordAttempts] == -1 { if Defaults[.gitPasswordAttempts] == -1 {
Defaults[.gitRepositoryPasswordAttempts] = 0 Defaults[.gitPasswordAttempts] = 0
return nil return nil
} }
Defaults[.gitRepositoryPasswordAttempts] += 1 Defaults[.gitPasswordAttempts] += 1
PasswordStore.shared.gitRepositoryPassword = newPassword PasswordStore.shared.gitPassword = newPassword
credential = try? GTCredential(userName: userName, password: newPassword) credential = try? GTCredential(userName: userName, password: newPassword)
case let .ssh(userName, password, publicKeyFile, privateKeyFile, passwordNotSetCallback): case let .ssh(userName, password, publicKeyFile, privateKeyFile, passwordNotSetCallback):
@ -76,7 +76,7 @@ struct GitCredential {
} }
// Save password for the future // Save password for the future
Utils.addPasswordToKeychain(name: "gitRepositorySSHPrivateKeyPassphrase", password: newPassword!) Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newPassword!)
// nil is expected in case of empty password // nil is expected in case of empty password
if newPassword == "" { if newPassword == "" {
@ -112,7 +112,7 @@ class PasswordStore {
var gitSignatureForNow: GTSignature { var gitSignatureForNow: GTSignature {
get { get {
return GTSignature(name: Defaults[.gitRepositoryUsername]!, email: Defaults[.gitRepositoryUsername]!+"@passforios", time: Date())! return GTSignature(name: Defaults[.gitUsername]!, email: Defaults[.gitUsername]!+"@passforios", time: Date())!
} }
} }
@ -126,12 +126,21 @@ class PasswordStore {
return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase") return Utils.getPasswordFromKeychain(name: "pgpKeyPassphrase")
} }
} }
var gitRepositoryPassword: String? { var gitPassword: String? {
set { set {
Utils.addPasswordToKeychain(name: "gitRepositoryPassword", password: newValue) Utils.addPasswordToKeychain(name: "gitPassword", password: newValue)
} }
get { get {
return Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") return Utils.getPasswordFromKeychain(name: "gitPassword")
}
}
var gitSSHPrivateKeyPassphrase: String? {
set {
Utils.addPasswordToKeychain(name: "gitSSHPrivateKeyPassphrase", password: newValue)
}
get {
return Utils.getPasswordFromKeychain(name: "gitSSHPrivateKeyPassphrase") ?? ""
} }
} }
@ -164,22 +173,41 @@ class PasswordStore {
print(error) print(error)
} }
initPGPKeys() initPGPKeys()
if Defaults[.gitRepositoryAuthenticationMethod] == "Password" { initGitCredential()
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitRepositoryUsername]!, password: Utils.getPasswordFromKeychain(name: "gitRepositoryPassword") ?? "")) }
} else if Defaults[.gitRepositoryAuthenticationMethod] == "SSH Key"{
enum SSHKeyType {
case `public`, secret
}
public func initGitCredential() {
if Defaults[.gitAuthenticationMethod] == "Password" {
gitCredential = GitCredential(credential: GitCredential.Credential.http(userName: Defaults[.gitUsername]!, password: Utils.getPasswordFromKeychain(name: "gitPassword") ?? ""))
} else if Defaults[.gitAuthenticationMethod] == "SSH Key"{
gitCredential = GitCredential( gitCredential = GitCredential(
credential: GitCredential.Credential.ssh( credential: GitCredential.Credential.ssh(
userName: Defaults[.gitRepositoryUsername]!, userName: Defaults[.gitUsername]!,
password: Utils.getPasswordFromKeychain(name: "gitRepositorySSHPrivateKeyPassphrase") ?? "", password: gitSSHPrivateKeyPassphrase ?? "",
publicKeyFile: Globals.sshPublicKeyURL, publicKeyFile: Globals.gitSSHPublicKeyURL,
privateKeyFile: Globals.sshPrivateKeyURL, privateKeyFile: Globals.gitSSHPrivateKeyURL,
passwordNotSetCallback: nil passwordNotSetCallback: nil
) )
) )
} else { } else {
gitCredential = nil gitCredential = nil
} }
}
public func initGitSSHKey(with armorKey: String, _ keyType: SSHKeyType) throws {
var keyPath = ""
switch keyType {
case .public:
keyPath = Globals.gitSSHPublicKeyPath
case .secret:
keyPath = Globals.gitSSHPrivateKeyPath
}
try armorKey.write(toFile: keyPath, atomically: true, encoding: .ascii)
} }
public func initPGPKeys() { public func initPGPKeys() {
@ -654,8 +682,8 @@ class PasswordStore {
Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath) Utils.removeFileIfExists(atPath: Globals.pgpPublicKeyPath)
Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath) Utils.removeFileIfExists(atPath: Globals.pgpPrivateKeyPath)
Utils.removeFileIfExists(at: Globals.sshPrivateKeyURL) Utils.removeFileIfExists(atPath: Globals.gitSSHPublicKeyPath)
Utils.removeFileIfExists(at: Globals.sshPublicKeyURL) Utils.removeFileIfExists(atPath: Globals.gitSSHPrivateKeyPath)
Utils.removeAllKeychain() Utils.removeAllKeychain()