Compare commits
No commits in common. "master" and "python" have entirely different histories.
31
.drone.yml
31
.drone.yml
@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: test
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: check
|
|
||||||
image: iamthefij/drone-pre-commit:personal
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: notify
|
|
||||||
|
|
||||||
depends_on:
|
|
||||||
- test
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
|
|
||||||
steps:
|
|
||||||
|
|
||||||
- name: notify
|
|
||||||
image: drillster/drone-email
|
|
||||||
settings:
|
|
||||||
host:
|
|
||||||
from_secret: SMTP_HOST # pragma: whitelist secret
|
|
||||||
username:
|
|
||||||
from_secret: SMTP_USER # pragma: whitelist secret
|
|
||||||
password:
|
|
||||||
from_secret: SMTP_PASS # pragma: whitelist secret
|
|
||||||
from: drone@iamthefij.com
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "ykoath"]
|
|
||||||
path = ykoath
|
|
||||||
url = https://github.com/vividboarder/ykoath
|
|
@ -1,13 +1,26 @@
|
|||||||
---
|
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||||
repos:
|
sha: 1553c96e2a0d0154f3aca4c5cb0156a74a8c703d
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.4.0
|
|
||||||
hooks:
|
hooks:
|
||||||
- id: check-added-large-files
|
- id: trailing-whitespace
|
||||||
- id: trailing-whitespace
|
- id: end-of-file-fixer
|
||||||
- id: end-of-file-fixer
|
- id: autopep8-wrapper
|
||||||
- id: check-merge-conflict
|
exclude: ./src/vendor/.+
|
||||||
- repo: https://github.com/golangci/golangci-lint
|
args:
|
||||||
rev: v1.52.2
|
- -i
|
||||||
|
- --ignore=E265,E309,E501
|
||||||
|
- id: debug-statements
|
||||||
|
exclude: ./src/vendor/.+
|
||||||
|
language_version: python2.7
|
||||||
|
- id: flake8
|
||||||
|
exclude: ./src/vendor/.+
|
||||||
|
language_version: python2.7
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-json
|
||||||
|
- id: name-tests-test
|
||||||
|
exclude: tests/(common.py|util.py|(helpers)/(.+).py)
|
||||||
|
- repo: git://github.com/asottile/reorder_python_imports
|
||||||
|
sha: ab609b9b982729dfc287b4e75963c0c4de254a31
|
||||||
hooks:
|
hooks:
|
||||||
- id: golangci-lint
|
- id: reorder-python-imports
|
||||||
|
exclude: ./src/vendor/.+
|
||||||
|
language_version: python2.7
|
||||||
|
19
LICENSE.txt
19
LICENSE.txt
@ -1,19 +0,0 @@
|
|||||||
Copyright (c) 2020 Ian Fijolek
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
48
Makefile
Normal file
48
Makefile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
.PHONY: default
|
||||||
|
default: run
|
||||||
|
|
||||||
|
.PHONY: build
|
||||||
|
build: venv install-ports
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: venv
|
||||||
|
./replace-workflow.sh
|
||||||
|
|
||||||
|
Yauth.alfredWorkflow: venv
|
||||||
|
mkdir Yauth.alfredWorkflow
|
||||||
|
cp -r alfred_yauth Yauth.alfredworkflow/
|
||||||
|
cp -r venv Yauth.alfredWorkflow/
|
||||||
|
cp info.plist Yauth.alfredWorkflow/
|
||||||
|
cp icon.png Yauth.alfredWorkflow/
|
||||||
|
|
||||||
|
# Installs required MacPorts
|
||||||
|
.PHONY: install-ports
|
||||||
|
install-ports:
|
||||||
|
sudo port install swig swig-python ykpers libu2f-host libusb
|
||||||
|
|
||||||
|
# Creates venv using MacPorts Python (Required for it to refrence libusb)
|
||||||
|
venv:
|
||||||
|
virtualenv --python=/opt/local/bin/python2.7 venv
|
||||||
|
./venv/bin/pip install -r ./requirements.txt
|
||||||
|
|
||||||
|
# Simple execution of the workflow to see all results
|
||||||
|
.PHONY: run
|
||||||
|
run: venv
|
||||||
|
@./venv/bin/python -m alfred_yauth.main
|
||||||
|
|
||||||
|
# Runs workflow and prompts for Yubikey password
|
||||||
|
.PHONY: set-password
|
||||||
|
set-password: venv
|
||||||
|
@./venv/bin/python -m alfred_yauth.main set-password
|
||||||
|
|
||||||
|
# Clears the virtualenv and other installed files
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -fr venv Yauth.alfredWorkflow
|
||||||
|
find . -name '*.pyc' -delete
|
||||||
|
find . -name '__pycache__' -delete
|
||||||
|
|
||||||
|
# Install precommit hooks
|
||||||
|
.PHONY: intall-hooks
|
||||||
|
install-hooks:
|
||||||
|
tox -e pre-commit -- install -f --install-hooks
|
34
README.md
34
README.md
@ -1,23 +1,37 @@
|
|||||||
# Alfred Yubico Auth
|
# Alfred Yubico Auth
|
||||||
|
|
||||||
This workflow allows quick searching and filling, and copying of OTP codes from a supported Yubikey.
|
An Alfred Workflow for auto filling authentication codes stored on your Yubikey.
|
||||||
|
|
||||||
So far, it has been tested with a Yubikey NEO on a MacBook Pro running macOS Catalina. I have no other devices to test with, but bug reports and patches may still be reviewed.
|
## Notes
|
||||||
|
|
||||||
## Cloning
|
This is definitely a work in progress. There are a lot of rough edges yet to be polished, but here it goes.
|
||||||
|
|
||||||
Currently this package depends on a fork of [yawn/ykoath](https://github.com/yawn/ykoath). To allow this to be built directly from this repo, the fork is added as a git submodule. This can be cloned using `git clone --recurse-submodules` or cloning normally and then executing `git submodule update --init`. Once the change has been merged upstream, the submodule and this notice will go away.
|
* Requires some to be installed with a package manager
|
||||||
|
* There is no way to input your key password through the UI yet. Do that with `make set-password` and then it should work fine.
|
||||||
|
* Error handling is terrible right now. If things don't work, check the debug log in Alfred
|
||||||
|
|
||||||
## Building
|
## Installation
|
||||||
|
|
||||||
Building requires [`mage`](https://magefile.org/)
|
Clone this repo
|
||||||
|
|
||||||
To see all targets and their descriptions, run `mage -l`. The most basic ones are as follows:
|
```bash
|
||||||
|
git clone https://git.iamthefij.com/iamthefij/alfred-yubico-auth.git
|
||||||
|
```
|
||||||
|
|
||||||
* `mage install`: Build and install into your local machine for testing
|
Either install your dependencies manually or, if you have MacPorts, you can use:
|
||||||
* `mage dist`: Build bundle for distribution
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make install-ports
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise you need to install `swig swig-python ykpers libu2f-host libusb` some other way.
|
||||||
|
|
||||||
|
Finally up the virtualenv and install to your Alfred with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
This uses [deanishe/awgo](https://github.com/deanishe/awgo) to interface with Alfred and [yawn/ykoath](https://github.com/yawn/ykoath) for interracting with the Yubikey
|
Uses the amazing [deanishe/alfred-workflow](https://github.com/deanishe/alfred-workflow) package
|
||||||
|
0
alfred_yauth/__init__.py
Normal file
0
alfred_yauth/__init__.py
Normal file
235
alfred_yauth/controller.py
Normal file
235
alfred_yauth/controller.py
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
import hashlib
|
||||||
|
from binascii import a2b_hex
|
||||||
|
from binascii import b2a_hex
|
||||||
|
|
||||||
|
from ykman.descriptor import get_descriptors
|
||||||
|
from ykman.driver_ccid import APDUError
|
||||||
|
from ykman.driver_otp import YkpersError
|
||||||
|
from ykman.oath import Credential
|
||||||
|
from ykman.oath import OathController
|
||||||
|
from ykman.oath import SW
|
||||||
|
from ykman.util import CAPABILITY
|
||||||
|
from ykman.util import derive_key
|
||||||
|
from ykman.util import parse_b32_key
|
||||||
|
from ykman.util import TRANSPORT
|
||||||
|
|
||||||
|
NON_FEATURE_CAPABILITIES = [CAPABILITY.CCID, CAPABILITY.NFC]
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceNotFoundError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CouldNotOpenDeviceError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(object):
|
||||||
|
_descriptor = None
|
||||||
|
_dev_info = None
|
||||||
|
|
||||||
|
def get_features(self):
|
||||||
|
return [
|
||||||
|
c.name for c in CAPABILITY if c not in NON_FEATURE_CAPABILITIES]
|
||||||
|
|
||||||
|
def count_devices(self):
|
||||||
|
return len(list(get_descriptors()))
|
||||||
|
|
||||||
|
def refresh(self):
|
||||||
|
descriptors = list(get_descriptors())
|
||||||
|
if len(descriptors) != 1:
|
||||||
|
self._descriptor = None
|
||||||
|
raise DeviceNotFoundError()
|
||||||
|
return
|
||||||
|
|
||||||
|
desc = descriptors[0]
|
||||||
|
if desc.fingerprint != (
|
||||||
|
self._descriptor.fingerprint if self._descriptor else None):
|
||||||
|
dev = desc.open_device()
|
||||||
|
if not dev:
|
||||||
|
raise CouldNotOpenDeviceError()
|
||||||
|
return
|
||||||
|
self._descriptor = desc
|
||||||
|
self._dev_info = {
|
||||||
|
'name': dev.device_name,
|
||||||
|
'version': '.'.join(str(x) for x in dev.version),
|
||||||
|
'serial': dev.serial or '',
|
||||||
|
'enabled': [c.name for c in CAPABILITY if c & dev.enabled],
|
||||||
|
'connections': [
|
||||||
|
t.name for t in TRANSPORT if t & dev.capabilities],
|
||||||
|
}
|
||||||
|
|
||||||
|
return self._dev_info
|
||||||
|
|
||||||
|
def refresh_credentials(self, timestamp, password_key=None):
|
||||||
|
return [
|
||||||
|
c.to_dict() for c in self._calculate_all(timestamp, password_key)]
|
||||||
|
|
||||||
|
def calculate(self, credential, timestamp, password_key):
|
||||||
|
return self._calculate(
|
||||||
|
Credential.from_dict(
|
||||||
|
credential), timestamp, password_key).to_dict()
|
||||||
|
|
||||||
|
def calculate_slot_mode(self, slot, digits, timestamp):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.OTP)
|
||||||
|
code = dev.driver.calculate(
|
||||||
|
slot, challenge=timestamp, totp=True, digits=int(digits),
|
||||||
|
wait_for_touch=True)
|
||||||
|
return Credential(
|
||||||
|
self._slot_name(slot), code=code, oath_type='totp', touch=True,
|
||||||
|
algo='SHA1', expiration=self._expiration(timestamp)).to_dict()
|
||||||
|
|
||||||
|
def refresh_slot_credentials(self, slots, digits, timestamp):
|
||||||
|
result = []
|
||||||
|
if slots[0]:
|
||||||
|
cred = self._read_slot_cred(1, digits[0], timestamp)
|
||||||
|
if cred:
|
||||||
|
result.append(cred)
|
||||||
|
if slots[1]:
|
||||||
|
cred = self._read_slot_cred(2, digits[1], timestamp)
|
||||||
|
if cred:
|
||||||
|
result.append(cred)
|
||||||
|
return [c.to_dict() for c in result]
|
||||||
|
|
||||||
|
def _read_slot_cred(self, slot, digits, timestamp):
|
||||||
|
try:
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.OTP)
|
||||||
|
code = dev.driver.calculate(
|
||||||
|
slot, challenge=timestamp, totp=True, digits=int(digits),
|
||||||
|
wait_for_touch=False)
|
||||||
|
return Credential(
|
||||||
|
self._slot_name(slot), code=code, oath_type='totp',
|
||||||
|
touch=False, algo='SHA1',
|
||||||
|
expiration=self._expiration(timestamp))
|
||||||
|
except YkpersError as e:
|
||||||
|
if e.errno == 11:
|
||||||
|
return Credential(
|
||||||
|
self._slot_name(slot), oath_type='totp', touch=True,
|
||||||
|
algo='SHA1')
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _slot_name(self, slot):
|
||||||
|
return "YubiKey Slot {}".format(slot)
|
||||||
|
|
||||||
|
def _expiration(self, timestamp):
|
||||||
|
return ((timestamp + 30) // 30) * 30
|
||||||
|
|
||||||
|
def needs_validation(self):
|
||||||
|
try:
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
return controller.locked
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_oath_id(self):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
return b2a_hex(controller.id).decode('utf-8')
|
||||||
|
|
||||||
|
def derive_key(self, password):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
key = derive_key(controller.id, password)
|
||||||
|
return b2a_hex(key).decode('utf-8')
|
||||||
|
|
||||||
|
def validate(self, key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if key is not None:
|
||||||
|
try:
|
||||||
|
controller.validate(a2b_hex(key))
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_password(self, new_password, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
if new_password is not None:
|
||||||
|
key = derive_key(controller.id, new_password)
|
||||||
|
controller.set_password(key)
|
||||||
|
else:
|
||||||
|
controller.clear_password()
|
||||||
|
|
||||||
|
def add_credential(
|
||||||
|
self, name, key, oath_type, digits, algo, touch, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
try:
|
||||||
|
key = parse_b32_key(key)
|
||||||
|
except Exception as e:
|
||||||
|
return str(e)
|
||||||
|
try:
|
||||||
|
controller.put(
|
||||||
|
key, name, oath_type, digits, algo=algo, require_touch=touch)
|
||||||
|
except APDUError as e:
|
||||||
|
# NEO doesn't return a no space error if full,
|
||||||
|
# but a command aborted error. Assume it's because of
|
||||||
|
# no space in this context.
|
||||||
|
if e.sw == SW.NO_SPACE or e.sw == SW.COMMAND_ABORTED:
|
||||||
|
return 'No space'
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def add_slot_credential(self, slot, key, touch):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.OTP)
|
||||||
|
key = parse_b32_key(key)
|
||||||
|
if len(key) > 64: # Keys longer than 64 bytes are hashed.
|
||||||
|
key = hashlib.sha1(key).digest()
|
||||||
|
if len(key) > 20:
|
||||||
|
raise ValueError(
|
||||||
|
'YubiKey Slots cannot handle TOTP keys over 20 bytes.')
|
||||||
|
key += b'\x00' * (20 - len(key)) # Keys must be padded to 20 bytes.
|
||||||
|
dev.driver.program_chalresp(int(slot), key, touch)
|
||||||
|
|
||||||
|
def delete_slot_credential(self, slot):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.OTP)
|
||||||
|
dev.driver.zap_slot(slot)
|
||||||
|
|
||||||
|
def delete_credential(self, credential, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
controller.delete(Credential.from_dict(credential))
|
||||||
|
|
||||||
|
def _calculate(self, credential, timestamp, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
cred = controller.calculate(credential, timestamp)
|
||||||
|
return cred
|
||||||
|
|
||||||
|
def _calculate_all(self, timestamp, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
creds = controller.calculate_all(timestamp)
|
||||||
|
creds = [c for c in creds if not c.hidden]
|
||||||
|
return creds
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
controller.reset()
|
||||||
|
|
||||||
|
def slot_status(self):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.OTP)
|
||||||
|
return list(dev.driver.slot_status)
|
||||||
|
|
||||||
|
def list_credentials(self, password_key):
|
||||||
|
dev = self._descriptor.open_device(TRANSPORT.CCID)
|
||||||
|
controller = OathController(dev.driver)
|
||||||
|
if controller.locked and password_key is not None:
|
||||||
|
controller.validate(a2b_hex(password_key))
|
||||||
|
creds = controller.list()
|
||||||
|
return [c.to_dict() for c in creds]
|
168
alfred_yauth/main.py
Normal file
168
alfred_yauth/main.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import sys
|
||||||
|
from getpass import getpass
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
from controller import APDUError
|
||||||
|
from controller import Controller
|
||||||
|
from controller import DeviceNotFoundError
|
||||||
|
from workflow import ICON_ACCOUNT
|
||||||
|
from workflow import ICON_ERROR
|
||||||
|
from workflow import Workflow3
|
||||||
|
|
||||||
|
|
||||||
|
YUBIKEY_CREDS_KEYCHAIN = 'yubico-auth-creds'
|
||||||
|
|
||||||
|
|
||||||
|
def cred_to_item_kwargs(cred):
|
||||||
|
if cred.get('hidden'):
|
||||||
|
return None
|
||||||
|
return {
|
||||||
|
'icon': ICON_ACCOUNT,
|
||||||
|
'title': cred['name'],
|
||||||
|
'subtitle': 'Copy to clipboard',
|
||||||
|
'copytext': cred['code'],
|
||||||
|
'arg': cred['code'],
|
||||||
|
'valid': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class YubicoAuth(Workflow3):
|
||||||
|
_controller = None
|
||||||
|
|
||||||
|
def get_controller(self):
|
||||||
|
if not self._controller:
|
||||||
|
self._controller = Controller()
|
||||||
|
self._controller.refresh()
|
||||||
|
return self._controller
|
||||||
|
|
||||||
|
def ask_yubikey_password(self):
|
||||||
|
"""Prompts the user for their Yubikey password and stores it"""
|
||||||
|
self.logger.debug('Set password')
|
||||||
|
password_key = self.get_controller().derive_key(
|
||||||
|
getpass('Yubikey Password:')
|
||||||
|
)
|
||||||
|
self.save_password(YUBIKEY_CREDS_KEYCHAIN, password_key)
|
||||||
|
|
||||||
|
self.get_controller().refresh_credentials(time(), password_key)
|
||||||
|
self.add_item(
|
||||||
|
'Yubikey password set successfully',
|
||||||
|
'',
|
||||||
|
icon=ICON_ACCOUNT,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_yubikey_password(self):
|
||||||
|
"""Returns stored Yubikey password from keychain"""
|
||||||
|
return self.get_password(YUBIKEY_CREDS_KEYCHAIN)
|
||||||
|
|
||||||
|
def _get_positional_arg(self, position):
|
||||||
|
"""Safely return a positional argument"""
|
||||||
|
if len(self.args) > position:
|
||||||
|
return self.args[position]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_command(self):
|
||||||
|
"""Get command out of the args as first parameter"""
|
||||||
|
return self._get_positional_arg(0)
|
||||||
|
|
||||||
|
def get_query(self):
|
||||||
|
"""Get query out of the args after first parameter"""
|
||||||
|
if len(self.args) < 2:
|
||||||
|
return None
|
||||||
|
return ' '.join(self.args[1:])
|
||||||
|
|
||||||
|
def _validate(self, command):
|
||||||
|
"""Validates that we can handle the current command"""
|
||||||
|
# if self.get_api_key() is None:
|
||||||
|
# self.add_item(
|
||||||
|
# title='Missing API key',
|
||||||
|
# subtitle='Set variable in settings',
|
||||||
|
# icon=ICON_ACCOUNT,
|
||||||
|
# valid=False,
|
||||||
|
# )
|
||||||
|
# return False
|
||||||
|
# if command == COMMAND_LOVE and self.get_recipient() is None:
|
||||||
|
# self.add_item(
|
||||||
|
# title='Recipient is required',
|
||||||
|
# icon=ICON_ERROR,
|
||||||
|
# valid=False,
|
||||||
|
# )
|
||||||
|
# return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _add_cred_to_results(self, cred):
|
||||||
|
self.logger.debug('Read {}'.format(cred.get('name')))
|
||||||
|
item_args = cred_to_item_kwargs(cred)
|
||||||
|
if item_args:
|
||||||
|
self.add_item(**item_args)
|
||||||
|
|
||||||
|
def list_credentials(self):
|
||||||
|
password_key = self.get_yubikey_password()
|
||||||
|
for cred in self.get_controller().list_credentials(password_key):
|
||||||
|
self._add_cred_to_results(cred)
|
||||||
|
|
||||||
|
def refresh_credentials(self):
|
||||||
|
key = self.get_yubikey_password()
|
||||||
|
for cred in self.get_controller().refresh_credentials(time(), key):
|
||||||
|
self._add_cred_to_results(cred)
|
||||||
|
|
||||||
|
def main(self):
|
||||||
|
self.logger.debug('Starting...')
|
||||||
|
command = self.get_command()
|
||||||
|
|
||||||
|
if not self._validate(command):
|
||||||
|
self.send_feedback()
|
||||||
|
return
|
||||||
|
|
||||||
|
command_action = None
|
||||||
|
if command == 'set-password':
|
||||||
|
command_action = self.ask_yubikey_password
|
||||||
|
elif command == 'list':
|
||||||
|
command_action = self.list_credentials
|
||||||
|
else:
|
||||||
|
command_action = self.refresh_credentials
|
||||||
|
|
||||||
|
try:
|
||||||
|
command_action()
|
||||||
|
except DeviceNotFoundError:
|
||||||
|
self.add_item(
|
||||||
|
'Could not find device',
|
||||||
|
'Is your Yubikey plugged in?',
|
||||||
|
icon=ICON_ERROR,
|
||||||
|
)
|
||||||
|
except APDUError:
|
||||||
|
self.add_item(
|
||||||
|
'Could not communicate with device',
|
||||||
|
'Is your Yubikey password set correctly?',
|
||||||
|
icon=ICON_ERROR,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.send_feedback()
|
||||||
|
|
||||||
|
|
||||||
|
def no_wf():
|
||||||
|
controller = Controller()
|
||||||
|
print(controller.get_features())
|
||||||
|
print(controller.count_devices())
|
||||||
|
print(controller.refresh())
|
||||||
|
|
||||||
|
password = getpass('YubiKey password?')
|
||||||
|
password_key = controller.derive_key(password)
|
||||||
|
timestamp = time()
|
||||||
|
print(controller.refresh_credentials(timestamp, password_key))
|
||||||
|
creds = controller.list_credentials(password_key)
|
||||||
|
|
||||||
|
print(creds)
|
||||||
|
|
||||||
|
|
||||||
|
def main(wf=None):
|
||||||
|
if wf is None:
|
||||||
|
no_wf()
|
||||||
|
else:
|
||||||
|
wf.main()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# main()
|
||||||
|
wf = YubicoAuth()
|
||||||
|
sys.exit(wf.run(main))
|
1
alfred_yauth/version
Normal file
1
alfred_yauth/version
Normal file
@ -0,0 +1 @@
|
|||||||
|
0.0.1
|
27
go.mod
27
go.mod
@ -1,27 +0,0 @@
|
|||||||
module git.iamthefij.com/iamthefij/alfred-yubico-auth
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
// Right now requires https://github.com/vividboarder/ykoath branch: validate
|
|
||||||
replace github.com/yawn/ykoath => ./ykoath
|
|
||||||
|
|
||||||
// Right now requires https://github.com/iamthefij/awgo branch: alfred-5
|
|
||||||
replace github.com/deanishe/awgo => github.com/iamthefij/awgo v0.29.1-pre1
|
|
||||||
|
|
||||||
require (
|
|
||||||
git.iamthefij.com/iamthefij/slog v1.0.0
|
|
||||||
github.com/deanishe/awgo v0.29.1
|
|
||||||
github.com/magefile/mage v1.14.0
|
|
||||||
github.com/yawn/ykoath v1.0.4
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/bmatcuk/doublestar v1.3.4 // indirect
|
|
||||||
github.com/ebfe/scard v0.0.0-20190212122703-c3d1b1916a95 // indirect
|
|
||||||
github.com/pkg/errors v0.8.1 // indirect
|
|
||||||
go.deanishe.net/env v0.5.1 // indirect
|
|
||||||
go.deanishe.net/fuzzy v1.0.0 // indirect
|
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 // indirect
|
|
||||||
golang.org/x/text v0.8.0 // indirect
|
|
||||||
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect
|
|
||||||
)
|
|
61
go.sum
61
go.sum
@ -1,61 +0,0 @@
|
|||||||
git.iamthefij.com/iamthefij/slog v1.0.0 h1:S+njoK+dr5VUYSopISHm2QMq3IwrHfwmi/CrAmhXVbg=
|
|
||||||
git.iamthefij.com/iamthefij/slog v1.0.0/go.mod h1:1RUj4hcCompZkAxXCRfUX786tb3cM/Zpkn97dGfUfbg=
|
|
||||||
github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
|
|
||||||
github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/ebfe/scard v0.0.0-20190212122703-c3d1b1916a95 h1:OM0MnUcXBysj7ZtXvThVWHMoahuKQ8FuwIdeSLcNdP4=
|
|
||||||
github.com/ebfe/scard v0.0.0-20190212122703-c3d1b1916a95/go.mod h1:8hHvF8DlEq5kE3KWOsZQezdWq1OTOVxZArZMscS954E=
|
|
||||||
github.com/iamthefij/awgo v0.29.1-pre1 h1:QrkVt0y3axBJl4tfdwHrsWXbDN7lsJKHNpqtp2WJOiA=
|
|
||||||
github.com/iamthefij/awgo v0.29.1-pre1/go.mod h1:1yGF+uQfWXX99TiDfAYYKjJpHTq5lHEmvHFEVCHo6KA=
|
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
|
||||||
github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
|
||||||
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
|
|
||||||
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
|
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
go.deanishe.net/env v0.5.1 h1:WiOncK5uJj8Um57Vj2dc1bq1lMN7fgRag9up7I3LZy0=
|
|
||||||
go.deanishe.net/env v0.5.1/go.mod h1:ihEYfDm0K0hq3f5ACTCQDrMTWxH9fTiA1lh1i0aMqm0=
|
|
||||||
go.deanishe.net/fuzzy v1.0.0 h1:3Qp6PCX0DLb9z03b5OHwAGsbRSkgJpSLncsiDdXDt4Y=
|
|
||||||
go.deanishe.net/fuzzy v1.0.0/go.mod h1:2yEEMfG7jWgT1s5EO0TteVWmx2MXFBRMr5cMm84bQNY=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
|
||||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
howett.net/plist v0.0.0-20201203080718-1454fab16a06 h1:QDxUo/w2COstK1wIBYpzQlHX/NqaQTcf9jyz347nI58=
|
|
||||||
howett.net/plist v0.0.0-20201203080718-1454fab16a06/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
|
751
info.plist
751
info.plist
@ -3,100 +3,14 @@
|
|||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>bundleid</key>
|
<key>bundleid</key>
|
||||||
<string>com.iamthefij.alfred-yubico-auth</string>
|
<string>com.vividboarder.alfred-yubico-auth</string>
|
||||||
<key>connections</key>
|
<key>connections</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>0149DE47-0232-434D-BB9E-B2C0B419A2E3</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>55DFD4B6-922A-4259-8625-EC14889FBACD</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>27BC434A-1663-44A7-85AF-C4AC6E1BEFB7</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>A8D2CCAC-5CA4-495E-BB62-5C7F596FA157</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>sourceoutputuid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>0149DE47-0232-434D-BB9E-B2C0B419A2E3</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>55DFD4B6-922A-4259-8625-EC14889FBACD</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>AD82ED59-033E-4860-B371-8128574E2FBC</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>sourceoutputuid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>506787F2-9A61-492C-8C49-30EE04FB70BC</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>8486DCAA-AFB7-407D-A0E9-E57E09997B24</key>
|
<key>8486DCAA-AFB7-407D-A0E9-E57E09997B24</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>destinationuid</key>
|
<key>destinationuid</key>
|
||||||
<string>DB934647-74DE-4182-ACDF-93C629AA99D9</string>
|
<string>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</string>
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>524288</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string>Show</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</string>
|
|
||||||
<key>modifiers</key>
|
<key>modifiers</key>
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
<key>modifiersubtext</key>
|
<key>modifiersubtext</key>
|
||||||
@ -106,11 +20,11 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>destinationuid</key>
|
<key>destinationuid</key>
|
||||||
<string>27BC434A-1663-44A7-85AF-C4AC6E1BEFB7</string>
|
<string>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</string>
|
||||||
<key>modifiers</key>
|
<key>modifiers</key>
|
||||||
<integer>1048576</integer>
|
<integer>1048576</integer>
|
||||||
<key>modifiersubtext</key>
|
<key>modifiersubtext</key>
|
||||||
<string>Paste code</string>
|
<string>Paste token</string>
|
||||||
<key>vitoclose</key>
|
<key>vitoclose</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
@ -128,56 +42,6 @@
|
|||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>A8D2CCAC-5CA4-495E-BB62-5C7F596FA157</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>DA99BA2E-7234-491D-BD0F-044151FA98E2</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>AD82ED59-033E-4860-B371-8128574E2FBC</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>F3AEDAF9-44BC-4E39-B908-724974ACA17B</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>sourceoutputuid</key>
|
|
||||||
<string>7B2EFCB5-ED69-4B10-84C5-4F6320C91491</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>sourceoutputuid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</key>
|
<key>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@ -204,44 +68,6 @@
|
|||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>DA99BA2E-7234-491D-BD0F-044151FA98E2</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>C252A5EC-1AEE-4EF4-864F-67483EAADCFA</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>sourceoutputuid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>506787F2-9A61-492C-8C49-30EE04FB70BC</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>DB934647-74DE-4182-ACDF-93C629AA99D9</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</key>
|
<key>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@ -255,22 +81,9 @@
|
|||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>destinationuid</key>
|
|
||||||
<string>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</string>
|
|
||||||
<key>modifiers</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>modifiersubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>vitoclose</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>createdby</key>
|
<key>createdby</key>
|
||||||
<string>Ian Fijolek</string>
|
<string>ViViDboarder</string>
|
||||||
<key>description</key>
|
<key>description</key>
|
||||||
<string>2FA for Yubikeys</string>
|
<string>2FA for Yubikeys</string>
|
||||||
<key>disabled</key>
|
<key>disabled</key>
|
||||||
@ -282,225 +95,20 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>config</key>
|
<key>config</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>argument</key>
|
<key>autopaste</key>
|
||||||
|
<false/>
|
||||||
|
<key>clipboardtext</key>
|
||||||
<string>{query}</string>
|
<string>{query}</string>
|
||||||
<key>passthroughargument</key>
|
<key>transient</key>
|
||||||
<false/>
|
<true/>
|
||||||
<key>variables</key>
|
|
||||||
<dict>
|
|
||||||
<key>result_action</key>
|
|
||||||
<string>show</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>type</key>
|
<key>type</key>
|
||||||
<string>alfred.workflow.utility.argument</string>
|
<string>alfred.workflow.output.clipboard</string>
|
||||||
<key>uid</key>
|
<key>uid</key>
|
||||||
<string>DB934647-74DE-4182-ACDF-93C629AA99D9</string>
|
<string>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</string>
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>lastpathcomponent</key>
|
|
||||||
<false/>
|
|
||||||
<key>onlyshowifquerypopulated</key>
|
|
||||||
<false/>
|
|
||||||
<key>removeextension</key>
|
|
||||||
<false/>
|
|
||||||
<key>text</key>
|
|
||||||
<string>Password key is now stored in your keychain</string>
|
|
||||||
<key>title</key>
|
|
||||||
<string>Password saved</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.output.notification</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>C252A5EC-1AEE-4EF4-864F-67483EAADCFA</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>concurrently</key>
|
|
||||||
<false/>
|
|
||||||
<key>escaping</key>
|
|
||||||
<integer>102</integer>
|
|
||||||
<key>script</key>
|
|
||||||
<string>./alfred-yubico-auth -run-script set-password</string>
|
|
||||||
<key>scriptargtype</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>scriptfile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>type</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.action.script</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>A8D2CCAC-5CA4-495E-BB62-5C7F596FA157</string>
|
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<integer>2</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>alfredfiltersresults</key>
|
|
||||||
<true/>
|
|
||||||
<key>alfredfiltersresultsmatchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>argumenttreatemptyqueryasnil</key>
|
|
||||||
<false/>
|
|
||||||
<key>argumenttrimmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>argumenttype</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>escaping</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>keyword</key>
|
|
||||||
<string>yubikey</string>
|
|
||||||
<key>queuedelaycustom</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
<key>queuedelayimmediatelyinitially</key>
|
|
||||||
<true/>
|
|
||||||
<key>queuedelaymode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>queuemode</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>runningsubtext</key>
|
|
||||||
<string></string>
|
|
||||||
<key>script</key>
|
|
||||||
<string>./alfred-yubico-auth list</string>
|
|
||||||
<key>scriptargtype</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>scriptfile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>subtext</key>
|
|
||||||
<string>Get 2FA tokens from Yubikey</string>
|
|
||||||
<key>title</key>
|
|
||||||
<string>Yubikey 2FA</string>
|
|
||||||
<key>type</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>withspace</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.input.scriptfilter</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>8486DCAA-AFB7-407D-A0E9-E57E09997B24</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>conditions</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>inputstring</key>
|
|
||||||
<string>{var:result}</string>
|
|
||||||
<key>matchcasesensitive</key>
|
|
||||||
<false/>
|
|
||||||
<key>matchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>matchstring</key>
|
|
||||||
<string>success</string>
|
|
||||||
<key>outputlabel</key>
|
|
||||||
<string>success</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>elselabel</key>
|
|
||||||
<string>else</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.conditional</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>DA99BA2E-7234-491D-BD0F-044151FA98E2</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>conditions</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>inputstring</key>
|
|
||||||
<string>{var:action}</string>
|
|
||||||
<key>matchcasesensitive</key>
|
|
||||||
<false/>
|
|
||||||
<key>matchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>matchstring</key>
|
|
||||||
<string>set-password</string>
|
|
||||||
<key>outputlabel</key>
|
|
||||||
<string>Set password</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>elselabel</key>
|
|
||||||
<string>else</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.conditional</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>argument</key>
|
|
||||||
<string>{query}</string>
|
|
||||||
<key>passthroughargument</key>
|
|
||||||
<false/>
|
|
||||||
<key>variables</key>
|
|
||||||
<dict>
|
|
||||||
<key>result_action</key>
|
|
||||||
<string>paste</string>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.argument</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>27BC434A-1663-44A7-85AF-C4AC6E1BEFB7</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>alignment</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>backgroundcolor</key>
|
|
||||||
<string></string>
|
|
||||||
<key>fadespeed</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>fillmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>font</key>
|
|
||||||
<string></string>
|
|
||||||
<key>ignoredynamicplaceholders</key>
|
|
||||||
<false/>
|
|
||||||
<key>largetypetext</key>
|
|
||||||
<string>{query}</string>
|
|
||||||
<key>textcolor</key>
|
|
||||||
<string></string>
|
|
||||||
<key>wrapat</key>
|
|
||||||
<integer>50</integer>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.output.largetype</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>F3AEDAF9-44BC-4E39-B908-724974ACA17B</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>config</key>
|
<key>config</key>
|
||||||
<dict>
|
<dict>
|
||||||
@ -522,69 +130,6 @@
|
|||||||
<key>version</key>
|
<key>version</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>conditions</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>inputstring</key>
|
|
||||||
<string>{var:result_action}</string>
|
|
||||||
<key>matchcasesensitive</key>
|
|
||||||
<false/>
|
|
||||||
<key>matchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>matchstring</key>
|
|
||||||
<string>show</string>
|
|
||||||
<key>outputlabel</key>
|
|
||||||
<string>show</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>7B2EFCB5-ED69-4B10-84C5-4F6320C91491</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>inputstring</key>
|
|
||||||
<string>{var:result_action}</string>
|
|
||||||
<key>matchcasesensitive</key>
|
|
||||||
<false/>
|
|
||||||
<key>matchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>matchstring</key>
|
|
||||||
<string>paste</string>
|
|
||||||
<key>outputlabel</key>
|
|
||||||
<string>paste</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>elselabel</key>
|
|
||||||
<string>copy</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.conditional</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>AD82ED59-033E-4860-B371-8128574E2FBC</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>autopaste</key>
|
|
||||||
<true/>
|
|
||||||
<key>clipboardtext</key>
|
|
||||||
<string>{query}</string>
|
|
||||||
<key>ignoredynamicplaceholders</key>
|
|
||||||
<false/>
|
|
||||||
<key>transient</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.output.clipboard</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>config</key>
|
<key>config</key>
|
||||||
<dict>
|
<dict>
|
||||||
@ -592,8 +137,6 @@
|
|||||||
<false/>
|
<false/>
|
||||||
<key>clipboardtext</key>
|
<key>clipboardtext</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>ignoredynamicplaceholders</key>
|
|
||||||
<false/>
|
|
||||||
<key>transient</key>
|
<key>transient</key>
|
||||||
<false/>
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
@ -602,95 +145,8 @@
|
|||||||
<key>uid</key>
|
<key>uid</key>
|
||||||
<string>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</string>
|
<string>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<integer>3</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>concurrently</key>
|
|
||||||
<false/>
|
|
||||||
<key>escaping</key>
|
|
||||||
<integer>102</integer>
|
|
||||||
<key>script</key>
|
|
||||||
<string>query=$1
|
|
||||||
|
|
||||||
./alfred-yubico-auth -run-script "$query"</string>
|
|
||||||
<key>scriptargtype</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
<key>scriptfile</key>
|
|
||||||
<string></string>
|
|
||||||
<key>type</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.action.script</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>0149DE47-0232-434D-BB9E-B2C0B419A2E3</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>2</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>seconds</key>
|
|
||||||
<string>10</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.delay</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>conditions</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>inputstring</key>
|
|
||||||
<string>{var:result}</string>
|
|
||||||
<key>matchcasesensitive</key>
|
|
||||||
<false/>
|
|
||||||
<key>matchmode</key>
|
|
||||||
<integer>0</integer>
|
|
||||||
<key>matchstring</key>
|
|
||||||
<string>success</string>
|
|
||||||
<key>outputlabel</key>
|
|
||||||
<string>success</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>94F60406-01FF-4991-A697-2C83147293EB</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>elselabel</key>
|
|
||||||
<string>else</string>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.utility.conditional</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>55DFD4B6-922A-4259-8625-EC14889FBACD</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>1</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>config</key>
|
|
||||||
<dict>
|
|
||||||
<key>autopaste</key>
|
|
||||||
<false/>
|
|
||||||
<key>clipboardtext</key>
|
|
||||||
<string>{query}</string>
|
|
||||||
<key>ignoredynamicplaceholders</key>
|
|
||||||
<false/>
|
|
||||||
<key>transient</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>type</key>
|
|
||||||
<string>alfred.workflow.output.clipboard</string>
|
|
||||||
<key>uid</key>
|
|
||||||
<string>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</string>
|
|
||||||
<key>version</key>
|
|
||||||
<integer>3</integer>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>config</key>
|
<key>config</key>
|
||||||
<dict>
|
<dict>
|
||||||
@ -715,158 +171,137 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>config</key>
|
<key>config</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>lastpathcomponent</key>
|
<key>alfredfiltersresults</key>
|
||||||
<false/>
|
|
||||||
<key>onlyshowifquerypopulated</key>
|
|
||||||
<true/>
|
<true/>
|
||||||
<key>removeextension</key>
|
<key>alfredfiltersresultsmatchmode</key>
|
||||||
<false/>
|
<integer>0</integer>
|
||||||
<key>text</key>
|
<key>argumenttrimmode</key>
|
||||||
<string>{query}</string>
|
<integer>0</integer>
|
||||||
|
<key>argumenttype</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>escaping</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>keyword</key>
|
||||||
|
<string>yubikey</string>
|
||||||
|
<key>queuedelaycustom</key>
|
||||||
|
<integer>3</integer>
|
||||||
|
<key>queuedelayimmediatelyinitially</key>
|
||||||
|
<true/>
|
||||||
|
<key>queuedelaymode</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>queuemode</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>runningsubtext</key>
|
||||||
|
<string></string>
|
||||||
|
<key>script</key>
|
||||||
|
<string>./venv/bin/python -m alfred_yauth.main</string>
|
||||||
|
<key>scriptargtype</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>scriptfile</key>
|
||||||
|
<string>main.py</string>
|
||||||
|
<key>subtext</key>
|
||||||
|
<string>Get 2FA tokens from Yubikey</string>
|
||||||
<key>title</key>
|
<key>title</key>
|
||||||
<string>Alfred Yubikey Error</string>
|
<string>Yubikey 2FA</string>
|
||||||
|
<key>type</key>
|
||||||
|
<integer>0</integer>
|
||||||
|
<key>withspace</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>type</key>
|
<key>type</key>
|
||||||
<string>alfred.workflow.output.notification</string>
|
<string>alfred.workflow.input.scriptfilter</string>
|
||||||
<key>uid</key>
|
<key>uid</key>
|
||||||
<string>506787F2-9A61-492C-8C49-30EE04FB70BC</string>
|
<string>8486DCAA-AFB7-407D-A0E9-E57E09997B24</string>
|
||||||
|
<key>version</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>config</key>
|
||||||
|
<dict>
|
||||||
|
<key>seconds</key>
|
||||||
|
<string>10</string>
|
||||||
|
</dict>
|
||||||
|
<key>type</key>
|
||||||
|
<string>alfred.workflow.utility.delay</string>
|
||||||
|
<key>uid</key>
|
||||||
|
<string>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<integer>1</integer>
|
<integer>1</integer>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>config</key>
|
||||||
|
<dict>
|
||||||
|
<key>autopaste</key>
|
||||||
|
<true/>
|
||||||
|
<key>clipboardtext</key>
|
||||||
|
<string>{query}</string>
|
||||||
|
<key>transient</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>type</key>
|
||||||
|
<string>alfred.workflow.output.clipboard</string>
|
||||||
|
<key>uid</key>
|
||||||
|
<string>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</string>
|
||||||
|
<key>version</key>
|
||||||
|
<integer>2</integer>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>readme</key>
|
<key>readme</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
<key>uidata</key>
|
<key>uidata</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>0149DE47-0232-434D-BB9E-B2C0B419A2E3</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>180</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>305</integer>
|
|
||||||
</dict>
|
|
||||||
<key>0718204D-3398-4AEF-A621-DDDE1FC6ED75</key>
|
<key>0718204D-3398-4AEF-A621-DDDE1FC6ED75</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>1245</integer>
|
<integer>960</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>225</integer>
|
<integer>140</integer>
|
||||||
</dict>
|
|
||||||
<key>27BC434A-1663-44A7-85AF-C4AC6E1BEFB7</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>260</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>155</integer>
|
|
||||||
</dict>
|
|
||||||
<key>2D69982A-0DB6-4ABA-812F-C7F2A73650AE</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>365</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>90</integer>
|
|
||||||
</dict>
|
|
||||||
<key>506787F2-9A61-492C-8C49-30EE04FB70BC</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>835</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>540</integer>
|
|
||||||
</dict>
|
|
||||||
<key>55DFD4B6-922A-4259-8625-EC14889FBACD</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>365</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>330</integer>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>8486DCAA-AFB7-407D-A0E9-E57E09997B24</key>
|
<key>8486DCAA-AFB7-407D-A0E9-E57E09997B24</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>70</integer>
|
<integer>120</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>65</integer>
|
<integer>140</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</key>
|
<key>9F48DDE6-BBE7-42ED-ABE5-C9255C92F1CD</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>650</integer>
|
<integer>360</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>415</integer>
|
<integer>140</integer>
|
||||||
</dict>
|
|
||||||
<key>A8D2CCAC-5CA4-495E-BB62-5C7F596FA157</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>515</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>55</integer>
|
|
||||||
</dict>
|
|
||||||
<key>AD82ED59-033E-4860-B371-8128574E2FBC</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>485</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>285</integer>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</key>
|
<key>BFB3A122-52BB-4FF1-B5B3-CECD42A730DB</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>1100</integer>
|
<integer>780</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>300</integer>
|
<integer>140</integer>
|
||||||
</dict>
|
|
||||||
<key>C252A5EC-1AEE-4EF4-864F-67483EAADCFA</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>800</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>45</integer>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>DA3E9CE8-7C4F-4B09-BFA5-F8CA83297968</key>
|
<key>DA3E9CE8-7C4F-4B09-BFA5-F8CA83297968</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>835</integer>
|
<integer>540</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>415</integer>
|
<integer>140</integer>
|
||||||
</dict>
|
|
||||||
<key>DA99BA2E-7234-491D-BD0F-044151FA98E2</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>670</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>75</integer>
|
|
||||||
</dict>
|
|
||||||
<key>DB934647-74DE-4182-ACDF-93C629AA99D9</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>260</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>40</integer>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</key>
|
<key>E86DC7C1-35C1-4BA9-8B33-A95DE7082F7E</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>1015</integer>
|
<integer>700</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>330</integer>
|
<integer>170</integer>
|
||||||
</dict>
|
|
||||||
<key>F3AEDAF9-44BC-4E39-B908-724974ACA17B</key>
|
|
||||||
<dict>
|
|
||||||
<key>xpos</key>
|
|
||||||
<integer>655</integer>
|
|
||||||
<key>ypos</key>
|
|
||||||
<integer>160</integer>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</key>
|
<key>FB0DDF80-FF90-439A-BF3F-6EC58C2AA870</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>xpos</key>
|
<key>xpos</key>
|
||||||
<integer>650</integer>
|
<integer>360</integer>
|
||||||
<key>ypos</key>
|
<key>ypos</key>
|
||||||
<integer>285</integer>
|
<integer>290</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>1.1.1</string>
|
<string>0.0.1</string>
|
||||||
<key>webaddress</key>
|
<key>webaddress</key>
|
||||||
<string></string>
|
<string></string>
|
||||||
</dict>
|
</dict>
|
||||||
|
146
magefile.go
146
magefile.go
@ -1,146 +0,0 @@
|
|||||||
//go:build mage
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/deanishe/awgo/util"
|
|
||||||
"github.com/deanishe/awgo/util/build"
|
|
||||||
"github.com/magefile/mage/mg"
|
|
||||||
"github.com/magefile/mage/sh"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
buildDir = "./build"
|
|
||||||
distDir = "./dist"
|
|
||||||
binName = "alfred-yubico-auth"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
info *build.Info
|
|
||||||
|
|
||||||
// Default mage target
|
|
||||||
Default = Run
|
|
||||||
|
|
||||||
// Output binary path
|
|
||||||
binPath = filepath.Join(buildDir, binName)
|
|
||||||
)
|
|
||||||
|
|
||||||
func InfoWithVersion(v int) build.Option {
|
|
||||||
return func(i *build.Info) {
|
|
||||||
i.AlfredMajorVersion = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
var err error
|
|
||||||
if info, err = build.NewInfo(InfoWithVersion(5)); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build workflow.
|
|
||||||
func Build() error {
|
|
||||||
mg.Deps(cleanBuild)
|
|
||||||
fmt.Println("Building...")
|
|
||||||
|
|
||||||
if err := sh.RunWith(info.Env(), "go", "build", "-o", binPath, "."); err != nil {
|
|
||||||
return fmt.Errorf("error building binary %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
globs := build.Globs(
|
|
||||||
"*.png",
|
|
||||||
"info.plist",
|
|
||||||
"README.md",
|
|
||||||
"LICENSE.txt",
|
|
||||||
"password-prompt.js",
|
|
||||||
)
|
|
||||||
|
|
||||||
return build.SymlinkGlobs(buildDir, globs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run workflow.
|
|
||||||
func Run() error {
|
|
||||||
mg.Deps(Build)
|
|
||||||
fmt.Println("Running...")
|
|
||||||
|
|
||||||
return sh.RunWith(info.Env(), binPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dist packages workflow for distribution.
|
|
||||||
func Dist() error {
|
|
||||||
mg.SerialDeps(Clean, Build)
|
|
||||||
fmt.Println("Exporting dist...")
|
|
||||||
|
|
||||||
p, err := build.Export(buildDir, distDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Exported %q\n", p)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install symlinked workflow to Alfred.
|
|
||||||
func Install() error {
|
|
||||||
mg.Deps(Build)
|
|
||||||
fmt.Printf("Installing (linking) %q to %q...\n", buildDir, info.InstallDir)
|
|
||||||
|
|
||||||
if err := sh.Rm(info.InstallDir); err != nil {
|
|
||||||
return fmt.Errorf("error cleaning previously installed workflow: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return build.Symlink(info.InstallDir, buildDir, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InstallHooks will install pre-commit hooks.
|
|
||||||
func InstallHooks() error {
|
|
||||||
return sh.RunV("pre-commit", "install", "--overwrite", "--install-hooks")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check will run all pre-commit hooks.
|
|
||||||
func Check() error {
|
|
||||||
return sh.RunV("pre-commit", "run", "--all-files")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean build files.
|
|
||||||
func Clean() error {
|
|
||||||
fmt.Println("Cleaning...")
|
|
||||||
mg.Deps(cleanBuild, cleanMage)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DistClean build files and distribution files.
|
|
||||||
func DistClean() error {
|
|
||||||
mg.Deps(Clean, cleanDist)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanDir(name string) error {
|
|
||||||
if !util.PathExists(name) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
infos, err := ioutil.ReadDir(name)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cleanDir could not read folder: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fi := range infos {
|
|
||||||
if err := sh.Rm(filepath.Join(name, fi.Name())); err != nil {
|
|
||||||
return fmt.Errorf("cleanDir could not remove file: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanBuild() error { return cleanDir(buildDir) }
|
|
||||||
func cleanDist() error { return cleanDir(distDir) }
|
|
||||||
func cleanMage() error { return sh.Run("mage", "-clean") }
|
|
179
main.go
179
main.go
@ -1,179 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.iamthefij.com/iamthefij/slog"
|
|
||||||
aw "github.com/deanishe/awgo"
|
|
||||||
"github.com/deanishe/awgo/util"
|
|
||||||
"github.com/yawn/ykoath"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
wf *aw.Workflow
|
|
||||||
oath *ykoath.OATH
|
|
||||||
keychainAccount = "yubico-auth-creds"
|
|
||||||
|
|
||||||
errIncorrectPassword = errors.New("incorrect password")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
wf = aw.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
wf.Run(run)
|
|
||||||
}
|
|
||||||
|
|
||||||
func promptPassword() (string, error) {
|
|
||||||
out, err := util.Run("./password-prompt.js")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("error reading password from prompt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
out = bytes.TrimRight(out, "\n")
|
|
||||||
|
|
||||||
return string(out), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPassword(s *ykoath.Select) error {
|
|
||||||
passphrase, err := promptPassword()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed reading passphrase: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validatePassphrase(s, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed validating passphrase: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = wf.Keychain.Set(keychainAccount, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed storing passphrase in keychain: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendResult(result string, args ...string) error {
|
|
||||||
results := aw.NewArgVars()
|
|
||||||
|
|
||||||
results.Arg(args...)
|
|
||||||
results.Var("result", result)
|
|
||||||
|
|
||||||
return results.Send()
|
|
||||||
}
|
|
||||||
|
|
||||||
func validatePassphrase(s *ykoath.Select, passphrase string) error {
|
|
||||||
key := s.DeriveKey(passphrase)
|
|
||||||
|
|
||||||
// verify password is correct with a validate call
|
|
||||||
ok, err := oath.Validate(s, key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error in validate: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
return errIncorrectPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() {
|
|
||||||
runScript := flag.Bool("run-script", false, "change output to script output")
|
|
||||||
|
|
||||||
wf.Args()
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if *runScript {
|
|
||||||
wf.Configure(aw.TextErrors(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
oath, err = ykoath.New()
|
|
||||||
if err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to iniatialize new oath: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
defer oath.Close()
|
|
||||||
oath.Debug = slog.Debug
|
|
||||||
|
|
||||||
// Select oath to begin
|
|
||||||
s, err := oath.Select()
|
|
||||||
if err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to select oath: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to see if we are trying to set a password
|
|
||||||
if flag.Arg(0) == "set-password" {
|
|
||||||
err = setPassword(s)
|
|
||||||
if err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to set password: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = sendResult("success"); err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to send password set result: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If required, authenticate with password from keychain
|
|
||||||
if s.Challenge != nil {
|
|
||||||
passphrase, err := wf.Keychain.Get(keychainAccount)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("no key found in keychain but password is required")
|
|
||||||
wf.NewWarningItem("No password set", "↵ to set password").
|
|
||||||
Var("action", "set-password").
|
|
||||||
Valid(true)
|
|
||||||
wf.SendFeedback()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = validatePassphrase(s, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("passphrase failed: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if flag.Arg(0) == "list" {
|
|
||||||
// List names only
|
|
||||||
names, err := oath.List()
|
|
||||||
if err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to list names: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, name := range names {
|
|
||||||
slog.Log(name.Name)
|
|
||||||
wf.NewItem(name.Name).
|
|
||||||
Icon(aw.IconAccount).
|
|
||||||
Subtitle("Copy to clipboard").
|
|
||||||
Arg(name.Name).
|
|
||||||
Valid(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name := flag.Arg(0)
|
|
||||||
|
|
||||||
code, err := oath.CalculateOne(name)
|
|
||||||
if err != nil {
|
|
||||||
// TODO: Check for error "requires-auth" and notify touch
|
|
||||||
wf.FatalError(fmt.Errorf("failed to generate code: %w", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
slog.Log(code)
|
|
||||||
|
|
||||||
if err = sendResult("success", code); err != nil {
|
|
||||||
wf.FatalError(fmt.Errorf("failed to send code: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !*runScript {
|
|
||||||
wf.SendFeedback()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#! /usr/bin/osascript
|
|
||||||
// https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/PromptforText.html#//apple_ref/doc/uid/TP40016239-CH80-SW1
|
|
||||||
function run(){
|
|
||||||
var app = Application.currentApplication()
|
|
||||||
app.includeStandardAdditions = true
|
|
||||||
var response = app.displayDialog(
|
|
||||||
"Enter your Yubikey passphrase",
|
|
||||||
{
|
|
||||||
defaultAnswer: "",
|
|
||||||
withIcon: "stop",
|
|
||||||
buttons: ["Cancel", "Save"],
|
|
||||||
defaultButton: "Save",
|
|
||||||
cancelButton: "Cancel",
|
|
||||||
givingUpAfter: 120,
|
|
||||||
hiddenAnswer: true
|
|
||||||
})
|
|
||||||
return response.textReturned
|
|
||||||
}
|
|
8
replace-workflow.sh
Executable file
8
replace-workflow.sh
Executable file
@ -0,0 +1,8 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Warning! This will remove the workflow at the provided path and replace it with a link to this directory"
|
||||||
|
read -p "Path to workflow to replace: " existing_workflow
|
||||||
|
|
||||||
|
rm -fr "$existing_workflow"
|
||||||
|
ln -s `pwd` "$existing_workflow"
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
alfred-workflow==1.27
|
||||||
|
yubikey-manager==0.4.0
|
@ -1,3 +1,4 @@
|
|||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
|
|
||||||
|
echo "$1" > ./alfred_yauth/version
|
||||||
plutil -replace version -string "$1" ./info.plist
|
plutil -replace version -string "$1" ./info.plist
|
||||||
|
32
setup.py
Normal file
32
setup.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from setuptools import find_packages
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
def read_version():
|
||||||
|
version_path = 'version'
|
||||||
|
try:
|
||||||
|
return open(
|
||||||
|
os.path.join(os.path.dirname(__file__), version_path)
|
||||||
|
).read()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='Yubico Auth Workflow',
|
||||||
|
version=read_version(),
|
||||||
|
description='Yubico Auth workflow for Alfred',
|
||||||
|
author='Ian Fijolek',
|
||||||
|
author_email='ian@iamthefij.com.com',
|
||||||
|
url='',
|
||||||
|
packages=find_packages(exclude=['tests*']),
|
||||||
|
install_requires=[],
|
||||||
|
license='MIT',
|
||||||
|
)
|
31
tox.ini
Normal file
31
tox.ini
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[tox]
|
||||||
|
envlist = py
|
||||||
|
indexserver =
|
||||||
|
default = https://pypi.python.org/simple/
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
deps = -rrequirements-dev.txt
|
||||||
|
|
||||||
|
[testenv:py]
|
||||||
|
deps = {[testenv]deps}
|
||||||
|
commands =
|
||||||
|
coverage run --source=./src/,tests/ -m pytest --strict {posargs}
|
||||||
|
coverage report -m
|
||||||
|
pre-commit run --all-files
|
||||||
|
|
||||||
|
[testenv:lint]
|
||||||
|
deps = {[testenv]deps}
|
||||||
|
flake8
|
||||||
|
commands = flake8 .
|
||||||
|
|
||||||
|
[testenv:pre-commit]
|
||||||
|
deps = pre-commit>=0.4.2
|
||||||
|
commands = pre-commit {posargs}
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
exclude = .svn,CVS,.bzr,.hg,.git,__pycache__,.ropeproject,.tox,docs,virtualenv_run
|
||||||
|
filename = *.py,*.wsgi
|
||||||
|
max-line-length = 80
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
norecursedirs = .* _darcs CVS docs virtualenv_run
|
1
ykoath
1
ykoath
@ -1 +0,0 @@
|
|||||||
Subproject commit fd081cb213d030585bfdd03305e03bff4d6e7a09
|
|
Loading…
Reference in New Issue
Block a user