opr 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b1792360804ff4bb0d545126ae92164e26c250bb78eb9970a8f449ceea2f6c73
4
+ data.tar.gz: d22b82e1d53ce14e79fc7b650ad558faf0f604fce75f92627924faa8ec3252e7
5
+ SHA512:
6
+ metadata.gz: 70ec8ea6492598f4afa3ca5406a61f3fd677334452637b69d2aaf28361ab2315223f325888850fad0b6ebeadbe535635348f9d2a8fe30cf6ee093199e3ce62d5
7
+ data.tar.gz: cd1e34f9844125d3d17d2ab67e611293a916c10c972334789c7163387daaa5fd2bb3b6c5edec96fc95b422fe2953404ecc6d22c3d02b35740b1946fc6f0e18ab
@@ -0,0 +1,13 @@
1
+ Gemfile.lock
2
+ /.bundle/
3
+ /.yardoc
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.swp
11
+
12
+ # rspec failure tracking
13
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,50 @@
1
+ require: rubocop-performance
2
+ # This configuration was generated by
3
+ # `rubocop --auto-gen-config`
4
+ # on 2016-05-16 14:49:14 -0700 using RuboCop version 0.40.0.
5
+ # The point is for the user to remove these configuration records
6
+ # one by one as the offenses are removed from the code base.
7
+ # Note that changes in the inspected code, or installation of new
8
+ # versions of RuboCop, may require this file to be generated again.
9
+
10
+ AllCops:
11
+ TargetRubyVersion: 2.5
12
+ DisplayCopNames: true
13
+ Exclude:
14
+ - 'tasks/**/*'
15
+ - 'vendor/**/*'
16
+
17
+ Style/CommentedKeyword:
18
+ Enabled: false
19
+
20
+ Metrics/AbcSize:
21
+ Max: 20
22
+
23
+ Metrics/ClassLength:
24
+ Max: 140
25
+
26
+ Metrics/CyclomaticComplexity:
27
+ Max: 10
28
+
29
+ Metrics/LineLength:
30
+ Max: 120
31
+ IgnoreCopDirectives: true
32
+ IgnoredPatterns: ["%{.*}$"]
33
+
34
+ Metrics/MethodLength:
35
+ Max: 15
36
+
37
+ Metrics/PerceivedComplexity:
38
+ Max: 11
39
+
40
+ Style/FrozenStringLiteralComment:
41
+ Enabled: false
42
+
43
+ Style/PercentLiteralDelimiters:
44
+ PreferredDelimiters:
45
+ # Using `[]` for string arrays instead of `()`, since normal arrays are
46
+ # indicated with `[]` not `()`.
47
+ '%w': '[]'
48
+ '%W': '[]'
49
+ '%i': '[]'
50
+ '%': '{}'
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.0
6
+ before_install: gem install bundler -v 2.1.0.pre.2
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at tj@rubyists.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubyists::opr.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Tj (bougyman) Vanderpoel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # Rubyists::Opr
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rubyists::opr`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'rubyists::opr'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install rubyists::opr
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rubyists::opr. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/rubyists::opr/blob/master/CODE_OF_CONDUCT.md).
36
+
37
+
38
+ ## Code of Conduct
39
+
40
+ Everyone interacting in the Rubyists::Opr project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/rubyists::opr/blob/master/CODE_OF_CONDUCT.md).
41
+
42
+ ## Copyright
43
+
44
+ Copyright (c) 2019 Tj (bougyman) Vanderpoel. See [MIT License](LICENSE.txt) for further details.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'rubyists::opr'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require 'pry'
11
+ Pry.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/opr ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ lib_path = File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
+ require 'rubyists::opr/cli'
7
+
8
+ Signal.trap('INT') do
9
+ warn("\n#{caller.join("\n")}: interrupted")
10
+ exit(1)
11
+ end
12
+
13
+ begin
14
+ Rubyists::Opr::CLI.start
15
+ rescue Rubyists::Opr::CLI::Error => e
16
+ puts "ERROR: #{e.message}"
17
+ exit 1
18
+ end
@@ -0,0 +1,54 @@
1
+ require 'rubyists::opr/version' # rubocop:disable Naming/FileName
2
+ require 'pathname'
3
+
4
+ module Rubyists
5
+ # Main namespace
6
+ module Opr
7
+ class Error < StandardError; end
8
+ ROOT = Pathname(__FILE__).dirname.expand_path.join('..').expand_path
9
+ LIBDIR = ROOT.join('lib/rubyists::opr')
10
+ MODEL_DIR = LIBDIR.join('model')
11
+
12
+ def self.with_login
13
+ yield
14
+ rescue TTY::Command::ExitError => e
15
+ raise unless e.to_s.match?(/not currently signed in|401: Authentication/)
16
+
17
+ warn 'You are not currently logged in to 1pass'
18
+ warn 'Consider running `eval (op signin)` in your shell to avoid logging in for each opr command'
19
+ Opr.login!
20
+ retry
21
+ end
22
+
23
+ def self.login!
24
+ out = `#{opbin} signin 2>&1`
25
+ raise "Problem logging in #{out}" if out.match? '(ERROR)'
26
+
27
+ firstline = out.split("\n").first.split('=')
28
+ key = firstline.first.split.last
29
+ val = JSON.parse(firstline.last)
30
+ ENV[key] = val
31
+ end
32
+
33
+ def self.opbin(bin_name: 'op')
34
+ raise Error, 'attribute bin_name cannot be nil' if bin_name.empty?
35
+
36
+ @opbin ||= `which #{bin_name}`.chomp
37
+ raise Error, '`op` binary not found' if @opbin.empty?
38
+
39
+ @opbin
40
+ end
41
+
42
+ def self.R(rbf) # rubocop:disable Naming/MethodName
43
+ require ROOT.join(rbf.to_s).to_s
44
+ end
45
+
46
+ def self.L(lib) # rubocop:disable Naming/MethodName
47
+ require LIBDIR.join(lib.to_s).to_s
48
+ end
49
+
50
+ def self.M(model) # rubocop:disable Naming/MethodName
51
+ require MODEL_DIR.join(model.to_s).to_s
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../rubyists::opr'
4
+ require 'thor'
5
+
6
+ module Rubyists
7
+ module Opr
8
+ # Handle the application command line parsing
9
+ # and the dispatch to various command objects
10
+ #
11
+ # @api public
12
+ class CLI < Thor
13
+ # Error raised by this runner
14
+ Error = Class.new(StandardError)
15
+
16
+ def self.exit_on_failure?
17
+ true
18
+ end
19
+
20
+ desc 'version', 'rubyists::opr version'
21
+ def version
22
+ require_relative 'version'
23
+ puts "v#{Rubyists::Opr::VERSION}"
24
+ end
25
+ map %w[--version -v] => :version
26
+
27
+ desc 'get ITEM', 'Get the contents of an item'
28
+ method_option :vault, aliases: '-v', type: :string, desc: 'Which vault the item is in'
29
+ method_option :help, aliases: '-h', type: :boolean,
30
+ desc: 'Display usage information'
31
+ def get(item)
32
+ if options[:help]
33
+ invoke :help, ['get']
34
+ else
35
+ require_relative 'commands/get'
36
+ Rubyists::Opr::Commands::Get.new(item, options).execute
37
+ end
38
+ end
39
+
40
+ require_relative 'commands/list'
41
+ register Rubyists::Opr::Commands::List, 'list', 'list [SUBCOMMAND]', 'List items/vaults'
42
+
43
+ desc 'gen [NAME | VAULT/NAME]',
44
+ <<~TXT
45
+ Generate a new password. If NAME or VAULT/NAME is given, store the generated pass, otherwise output it.
46
+ if STDIN is passed, will use those characters as the special characters.
47
+ TXT
48
+ method_option :min, type: :numeric, desc: 'Minimum Pass size', default: 8, aliases: ['-m']
49
+ method_option :max, type: :numeric, desc: 'Maximum Pass size', aliases: ['-M']
50
+ method_option :size, type: :numeric, desc: 'Exact Pass size', aliases: ['-s']
51
+ method_option :chars, type: :boolean, desc: '(Do not) Use Special Chars', default: true
52
+ method_option :vault, type: :string, desc: 'Vault to save password in', default: 'Private'
53
+ method_option :type, type: :string, enum: %w[login note], default: 'login',
54
+ desc: 'The type of item to store'
55
+
56
+ method_option :help, aliases: '-h', type: :boolean,
57
+ desc: 'Display usage information'
58
+ def gen(path = nil)
59
+ if options[:help]
60
+ invoke :help, ['gen']
61
+ else
62
+ require_relative 'commands/gen'
63
+ Rubyists::Opr::Commands::Gen.new(path, options).execute
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'highline'
5
+ require 'json'
6
+
7
+ module Rubyists
8
+ module Opr
9
+ # Command class
10
+ class Command
11
+ extend Forwardable
12
+
13
+ attr_reader :options
14
+ def_delegators :command, :run
15
+ # Execute this command
16
+ #
17
+ # @api public
18
+ def execute(*)
19
+ raise(
20
+ NotImplementedError,
21
+ "#{self.class}##{__method__} must be implemented"
22
+ )
23
+ end
24
+
25
+ # The external commands runner
26
+ #
27
+ # @see http://www.rubydoc.info/gems/tty-command
28
+ #
29
+ # @api public
30
+ def command(**options)
31
+ require 'tty-command'
32
+ TTY::Command.new(options)
33
+ end
34
+
35
+ # The cursor movement
36
+ #
37
+ # @see http://www.rubydoc.info/gems/tty-cursor
38
+ #
39
+ # @api public
40
+ def cursor
41
+ require 'tty-cursor'
42
+ TTY::Cursor
43
+ end
44
+
45
+ # Open a file or text in the user's preferred editor
46
+ #
47
+ # @see http://www.rubydoc.info/gems/tty-editor
48
+ #
49
+ # @api public
50
+ def editor
51
+ require 'tty-editor'
52
+ TTY::Editor
53
+ end
54
+
55
+ # File manipulation utility methods
56
+ #
57
+ # @see http://www.rubydoc.info/gems/tty-file
58
+ #
59
+ # @api public
60
+ def generator
61
+ require 'tty-file'
62
+ TTY::File
63
+ end
64
+
65
+ # Terminal output paging
66
+ #
67
+ # @see http://www.rubydoc.info/gems/tty-pager
68
+ #
69
+ # @api public
70
+ def pager(**options)
71
+ require 'tty-pager'
72
+ TTY::Pager.new(options)
73
+ end
74
+
75
+ # Terminal platform and OS properties
76
+ #
77
+ # @see http://www.rubydoc.info/gems/tty-pager
78
+ #
79
+ # @api public
80
+ def platform
81
+ require 'tty-platform'
82
+ TTY::Platform.new
83
+ end
84
+
85
+ # The interactive prompt
86
+ #
87
+ # @see http://www.rubydoc.info/gems/tty-prompt
88
+ #
89
+ # @api public
90
+ def prompt(**options)
91
+ require 'tty-prompt'
92
+ TTY::Prompt.new(options)
93
+ end
94
+
95
+ # Get terminal screen properties
96
+ #
97
+ # @see http://www.rubydoc.info/gems/tty-screen
98
+ #
99
+ # @api public
100
+ def screen
101
+ require 'tty-screen'
102
+ TTY::Screen
103
+ end
104
+
105
+ # The unix which utility
106
+ #
107
+ # @see http://www.rubydoc.info/gems/tty-which
108
+ #
109
+ # @api public
110
+ def which(*args)
111
+ require 'tty-which'
112
+ TTY::Which.which(*args)
113
+ end
114
+
115
+ # Check if executable exists
116
+ #
117
+ # @see http://www.rubydoc.info/gems/tty-which
118
+ #
119
+ # @api public
120
+ def exec_exist?(*args)
121
+ require 'tty-which'
122
+ TTY::Which.exist?(*args)
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../rubyists::opr'
4
+
5
+ module Rubyists
6
+ # Main namespace
7
+ module Opr
8
+ L :command
9
+ L :utils
10
+ module Commands
11
+ # Generate passwords
12
+ class Gen < Rubyists::Opr::Command
13
+ attr_reader :path_name
14
+ def initialize(path, options)
15
+ @path_name = path
16
+ @options = options
17
+ end
18
+
19
+ # Here we determine if the @path contains a vault
20
+ # or is just a name.
21
+ def parse_path
22
+ # If options[:vault] is supplied, consider the whole @path the item name
23
+ return OpenStruct.new(vault: options[:vault], name: path_name) if options[:vault]
24
+
25
+ # Otherwise see if the path starts with a vault
26
+ parts = path_name.split('/', 2)
27
+ # If we got 2 parts, the first is the vault, the rest is the item name
28
+ if parts.size == 2
29
+ vault, name = parts
30
+ else
31
+ name = path_name
32
+ vault = options[:vault] || 'Private'
33
+ end
34
+ OpenStruct.new vault: vault, name: name
35
+ end
36
+
37
+ def output_or_save(pass, output: $stdout)
38
+ return output.puts(pass) if path_name.nil?
39
+
40
+ parsed = parse_path
41
+ Opr.with_login do
42
+ vault = Vault.find_by_name(parsed.vault)
43
+ vault.insert(name: parsed.name, type: :login, password: pass, username: options[:username])
44
+ end
45
+ output.puts "#{parsed.name} saved in #{parsed.vault}"
46
+ end
47
+
48
+ def execute(input: $stdin, output: $stdout)
49
+ chars = begin
50
+ str = input.read_nonblock(1) << input.read.chomp
51
+ str.chars
52
+ rescue IO::EAGAINWaitReadable
53
+ Opr::Utils::SAMPLES
54
+ end
55
+ chars = nil if options[:chars] == false
56
+ if options[:size]
57
+ min = max = options[:size]
58
+ else
59
+ min, max = options.values_at :min, :max
60
+ end
61
+ output_or_save Utils.passgen(min, max, chars), output: output
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../command'
4
+
5
+ module Rubyists
6
+ module Opr
7
+ module Commands
8
+ # 'get' command
9
+ class Get < Rubyists::Opr::Command
10
+ def initialize(item, options)
11
+ @item = item
12
+ @options = options
13
+ end
14
+
15
+ def execute(input: $stdin, output: $stdout) # rubocop:disable Lint/UnusedMethodArgument
16
+ if options[:vault]
17
+ vault = options[:vault]
18
+ else
19
+ split = @item.split('/', 2)
20
+ vault, @item = split if split.size == 2
21
+ end
22
+ if vault.nil?
23
+ warn 'No vault specified, using "Private"'
24
+ vault = 'Private'
25
+ end
26
+ Opr.with_login { output.puts Item.find(@item, vault: vault) }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Rubyists
6
+ # Main namespace
7
+ module Opr
8
+ M :vault
9
+ M :item
10
+ module Commands
11
+ # List commands
12
+ class List < Thor
13
+ namespace :list
14
+
15
+ desc 'items [VAULT]', 'List Items in VAULT. Defaults to "Private" if no VAULT given'
16
+ method_option :help, aliases: '-h', type: :boolean,
17
+ desc: 'Display usage information'
18
+ def items(vault = nil)
19
+ if options[:help]
20
+ invoke :help, ['items']
21
+ else
22
+ require_relative 'list/items'
23
+ Rubyists::Opr::Commands::List::Items.new(vault, options).execute
24
+ end
25
+ end
26
+
27
+ desc 'vaults', 'List vaults'
28
+ method_option :help, aliases: '-h', type: :boolean,
29
+ desc: 'Display usage information'
30
+ def vaults(*)
31
+ if options[:help]
32
+ invoke :help, ['vaults']
33
+ else
34
+ require_relative 'list/vaults'
35
+ Rubyists::Opr::Commands::List::Vaults.new(options).execute
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+
5
+ module Rubyists
6
+ module Opr
7
+ module Commands
8
+ class List
9
+ # Items subcommand
10
+ class Items < Rubyists::Opr::Command
11
+ attr_reader :vault
12
+ def initialize(vault, options)
13
+ @vault = vault
14
+ @options = options
15
+ end
16
+
17
+ def execute(input: $stdin, output: $stdout) # rubocop:disable Lint/UnusedMethodArgument
18
+ if vault.nil?
19
+ warn 'Using vault "Private" since none was given'
20
+ @vault = 'Private'
21
+ end
22
+ # Command logic goes here ...
23
+ Opr.with_login { output.puts Vault.find_by_name(vault).items.map(&:title) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+
5
+ module Rubyists
6
+ # Main namespace
7
+ module Opr
8
+ M :vault
9
+ module Commands
10
+ class List
11
+ # List Vaults command
12
+ class Vaults < Rubyists::Opr::Command
13
+ def initialize(options)
14
+ @options = options
15
+ end
16
+
17
+ def execute(input: $stdin, output: $stdout) # rubocop:disable Lint/UnusedMethodArgument
18
+ Opr.with_login { output.puts Vault.all.map(&:name) }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ module Rubyists
2
+ module Opr
3
+ # An item
4
+ class Item
5
+ def self.from_output(string)
6
+ from_hash JSON.parse(string)
7
+ end
8
+
9
+ def self.from_hash(hash)
10
+ new uuid: hash['uuid'], name: hash['overview']['title'], raw: hash
11
+ end
12
+
13
+ def self.find(item, vault:)
14
+ cmd = TTY::Command.new pty: true, printer: :null
15
+ out, err = cmd.run "#{Opr.opbin} get item '#{item}' --vault='#{vault}'"
16
+ raise Error, err unless err.empty?
17
+
18
+ item = from_output out
19
+ item.password
20
+ end
21
+
22
+ attr_reader :name, :uuid, :raw
23
+ def initialize(uuid:, name:, raw:)
24
+ @name = name
25
+ @uuid = uuid
26
+ @raw = raw
27
+ end
28
+
29
+ def password
30
+ pass_field = @raw['details']['fields'].detect { |f| f['designation'] == 'password' }
31
+ pass_field['value']
32
+ end
33
+
34
+ def title
35
+ @title ||= raw['overview']['title']
36
+ end
37
+
38
+ def inspect
39
+ "<#{self.class}:#{object_id} name: #{name} uuid: #{uuid}>"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ require 'tty-command'
2
+ module Rubyists
3
+ module Opr
4
+ # Represents a 1password vault
5
+ class Vault
6
+ def self.from_output(string)
7
+ from_hash JSON.parse(string)
8
+ end
9
+
10
+ def self.from_hash(hash)
11
+ new uuid: hash['uuid'], name: hash['name']
12
+ end
13
+
14
+ def self.all
15
+ cmd = TTY::Command.new pty: true, printer: :null
16
+ out, err = cmd.run Opr.opbin, 'list', 'vaults'
17
+ vaults = JSON.parse out
18
+ vaults.map { |v| from_hash v }
19
+ end
20
+
21
+ def self.find_by_name(name)
22
+ all.detect { |n| n.name == name }
23
+ end
24
+
25
+ attr_reader :name, :uuid
26
+ def initialize(uuid:, name:)
27
+ @name = name
28
+ @uuid = uuid
29
+ end
30
+
31
+ def insert(name:, password:, type: 'login', username: nil, notes: nil)
32
+ current = Item.find(name, vault: self.name)
33
+ raise "There is already an item named #{name} in the #{self.name} vault" if current
34
+
35
+ tpl = Opr::LIBDIR.join("commands/templates/gen/#{type}.erb")
36
+ erb = ERB.new(tpl.read)
37
+ hash = JSON.parse erb.result(binding)
38
+ Item.create!(hash, vault: uuid, name: name)
39
+ end
40
+
41
+ def items
42
+ return @items if @items
43
+
44
+ cmd = TTY::Command.new pty: true, printer: :null
45
+ out, err = cmd.run "#{Opr.opbin} list items --vault='#{name}'"
46
+ array = JSON.parse out
47
+ @items = array.map { |h| Item.from_hash h }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ {
2
+ "notesPlain": "<%= notes %>",
3
+ "sections": [],
4
+ "passwordHistory": [],
5
+ "fields": [
6
+ {
7
+ "value": "<%= username %>",
8
+ "name": "username",
9
+ "type": "T",
10
+ "designation": "username"
11
+ },
12
+ {
13
+ "value": "<%= password %>",
14
+ "name": "password",
15
+ "type": "P",
16
+ "designation": "password"
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,79 @@
1
+ require 'securerandom'
2
+ require 'base64'
3
+
4
+ module Rubyists # {{{
5
+ # Main namespace
6
+ module Opr # {{{
7
+ def self.s_or_no(count) # {{{
8
+ count == 1 ? '' : 's'
9
+ end # }}}
10
+
11
+ # Utility methods
12
+ module Utils # {{{
13
+ Doofus = Class.new Opr::Error
14
+ MAX_PASS_SIZE = 8192
15
+ SAMPLES = ((' '..'@').to_a + ('['..'`').to_a + ('{'..'~').to_a).freeze
16
+ SAFE_CHARS = (('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a).freeze
17
+
18
+ # Decode a Base64 string
19
+ def self.decode(str) # {{{
20
+ Base64.decode64(str)
21
+ end # }}}
22
+
23
+ # Encode an object for 1password POST-ing. obj must respond to #to_json
24
+ def self.encode(obj) # {{{
25
+ doofus! "#{obj} does not respond to #to_json" unless obj.respond_to? :to_json
26
+
27
+ Base64.encode64(obj.to_json).tr("\n", '').sub(/=+$/, '')
28
+ end # }}}
29
+
30
+ def self.doofus!(message, code: 1) # {{{
31
+ raise Doofus, message
32
+ rescue Doofus => e
33
+ warn "Doofus Error! <<#{e}>>"
34
+ exit code
35
+ end # }}}
36
+
37
+ def self.chunk_size(size) # {{{
38
+ if size > 20
39
+ 4..6
40
+ elsif size >= 12
41
+ 3..5
42
+ elsif size >= 8
43
+ 2..3
44
+ else
45
+ 1..2
46
+ end.to_a
47
+ end # }}}
48
+
49
+ def self.shuffle_and_right_size(arr, size) # {{{
50
+ flattened = arr.flatten
51
+ if flattened.size < size
52
+ until flattened.size == size # rubocop:disable Style/WhileUntilModifier
53
+ flattened << SAFE_CHARS.sample
54
+ end
55
+ end
56
+ flattened.sort { |_, _| rand(10) >= 5 ? 1 : -1 }.join('')[0, size]
57
+ end # }}}
58
+
59
+ # Generate a password
60
+ def self.passgen(minsize, maxsize = nil, chars = SAMPLES) # {{{
61
+ doofus! "What's the point in a #{minsize} character pass? Minimum is 6" if minsize < 6
62
+ maxsize ||= 16
63
+ doofus! "You're going to break something with a max size of #{maxsize} Max is #{MAX_PASS_SIZE}" if maxsize > MAX_PASS_SIZE # rubocop:disable Metrics/LineLength
64
+
65
+ maxsize = minsize if minsize > maxsize
66
+ size = (minsize..maxsize).to_a.sample
67
+ chunks = chunk_size size
68
+ rand = chars ? SecureRandom.base64(size) : SecureRandom.urlsafe_base64(size)
69
+ arr = rand.sub(/=+$/, '').chars.each_slice(chunks.sample).map do |e|
70
+ joined = e.join
71
+ joined << chars.sample if chars
72
+ joined
73
+ end
74
+ shuffle_and_right_size arr, size
75
+ end # }}}
76
+ end # }}}
77
+ end # }}}
78
+ end # }}}
79
+ # vim: set et sts=2 sw=2 ts=2 syntax=ruby foldmethod=marker:
@@ -0,0 +1,5 @@
1
+ module Rubyists
2
+ module Opr
3
+ VERSION = '0.2.0'.freeze
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ require_relative 'lib/rubyists::opr/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'opr'
5
+ spec.version = Rubyists::Opr::VERSION
6
+ spec.authors = ['Tj (bougyman) Vanderpoel']
7
+ spec.license = 'MIT'
8
+ spec.email = ['tj@rubyists.com']
9
+
10
+ spec.summary = 'A wrapper for the `op` 1password cli.'
11
+ spec.description = 'This utility/library exposes the 1password `op` cli in an easier to digest form.'
12
+ spec.homepage = 'https://gitlab.com/rubyists/opr.'
13
+
14
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
15
+
16
+ spec.metadata['homepage_uri'] = spec.homepage
17
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/rubyists/opr.'
18
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/rubyists/opr/CHANGELOG.md.'
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_dependency 'highline'
30
+ spec.add_dependency 'pry'
31
+ spec.add_dependency 'thor'
32
+ spec.add_dependency 'tty'
33
+ spec.add_dependency 'tty-prompt'
34
+ spec.add_dependency 'tty-command'
35
+ spec.add_development_dependency 'bundler', '< 2.0'
36
+ spec.add_development_dependency 'rake', '~> 10.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'rubocop-performance'
39
+ end
metadata ADDED
@@ -0,0 +1,221 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Tj (bougyman) Vanderpoel
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-12-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: tty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: tty-prompt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: tty-command
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "<"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "<"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-performance
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ description: This utility/library exposes the 1password `op` cli in an easier to digest
154
+ form.
155
+ email:
156
+ - tj@rubyists.com
157
+ executables:
158
+ - opr
159
+ extensions: []
160
+ extra_rdoc_files: []
161
+ files:
162
+ - ".gitignore"
163
+ - ".rspec"
164
+ - ".rubocop.yml"
165
+ - ".travis.yml"
166
+ - CODE_OF_CONDUCT.md
167
+ - Gemfile
168
+ - LICENSE.txt
169
+ - README.adoc
170
+ - Rakefile
171
+ - bin/console
172
+ - bin/setup
173
+ - exe/opr
174
+ - lib/rubyists::opr.rb
175
+ - lib/rubyists::opr/cli.rb
176
+ - lib/rubyists::opr/command.rb
177
+ - lib/rubyists::opr/commands/.gitkeep
178
+ - lib/rubyists::opr/commands/gen.rb
179
+ - lib/rubyists::opr/commands/get.rb
180
+ - lib/rubyists::opr/commands/list.rb
181
+ - lib/rubyists::opr/commands/list/items.rb
182
+ - lib/rubyists::opr/commands/list/vaults.rb
183
+ - lib/rubyists::opr/model/item.rb
184
+ - lib/rubyists::opr/model/vault.rb
185
+ - lib/rubyists::opr/templates/.gitkeep
186
+ - lib/rubyists::opr/templates/gen/.gitkeep
187
+ - lib/rubyists::opr/templates/gen/login.erb
188
+ - lib/rubyists::opr/templates/get/.gitkeep
189
+ - lib/rubyists::opr/templates/list/items/.gitkeep
190
+ - lib/rubyists::opr/templates/list/vaults/.gitkeep
191
+ - lib/rubyists::opr/utils.rb
192
+ - lib/rubyists::opr/version.rb
193
+ - rubyists::opr.gemspec
194
+ homepage: https://gitlab.com/rubyists/opr.
195
+ licenses:
196
+ - MIT
197
+ metadata:
198
+ allowed_push_host: https://rubygems.org
199
+ homepage_uri: https://gitlab.com/rubyists/opr.
200
+ source_code_uri: https://gitlab.com/rubyists/opr.
201
+ changelog_uri: https://gitlab.com/rubyists/opr/CHANGELOG.md.
202
+ post_install_message:
203
+ rdoc_options: []
204
+ require_paths:
205
+ - lib
206
+ required_ruby_version: !ruby/object:Gem::Requirement
207
+ requirements:
208
+ - - ">="
209
+ - !ruby/object:Gem::Version
210
+ version: '0'
211
+ required_rubygems_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ requirements: []
217
+ rubygems_version: 3.0.3
218
+ signing_key:
219
+ specification_version: 4
220
+ summary: A wrapper for the `op` 1password cli.
221
+ test_files: []