loginx 1.0.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
+ SHA1:
3
+ metadata.gz: 2a39d45a836f01215b06403c3ecb382e651aa2d9
4
+ data.tar.gz: 85c4dc680222bdef25894b9e4dc1c874f6c66175
5
+ SHA512:
6
+ metadata.gz: 09a148f51b15c872d9e2295428406a4780f15364e04f1b60e23b9fd5f25991aa30ec0bd35419cd3ccb8bccd09df1c33ed62947ad60206b3169937f449bbc7f77
7
+ data.tar.gz: 1bf6996c22404499b22949fab723dec362f37ad158b9f6c8b9e5a618ca4e2bfb4dfedab31eead92c5e17bef038625a64b065a21501df1335303942ad78ba9a0e
@@ -0,0 +1,54 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+ /.idea/*
13
+ .idea/workspace.xml
14
+
15
+ # Used by dotenv library to load environment variables.
16
+ # .env
17
+
18
+ ## Specific to RubyMotion:
19
+ .dat*
20
+ .repl_history
21
+ build/
22
+ *.bridgesupport
23
+ build-iPhoneOS/
24
+ build-iPhoneSimulator/
25
+
26
+ ## Specific to RubyMotion (use of CocoaPods):
27
+ #
28
+ # We recommend against adding the Pods directory to your .gitignore. However
29
+ # you should judge for yourself, the pros and cons are mentioned at:
30
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
31
+ #
32
+ # vendor/Pods/
33
+
34
+ ## Documentation cache and generated files:
35
+ /.yardoc/
36
+ /_yardoc/
37
+ /doc/
38
+ /rdoc/
39
+
40
+ ## Environment normalization:
41
+ /.bundle/
42
+ /vendor/bundle
43
+
44
+
45
+ # for a library or gem, you might want to ignore these files since the code is
46
+ # intended to run in multiple environments; otherwise, check them in:
47
+ # Gemfile.lock
48
+ # .ruby-version
49
+ # .ruby-gemset
50
+
51
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
52
+ .rvmrc
53
+
54
+ .idea/
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in loginx.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,25 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ loginx (1.0.0)
5
+ methadone (>= 1.9.3)
6
+ ruby_expect
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ methadone (1.9.3)
12
+ bundler
13
+ rake (11.3.0)
14
+ ruby_expect (1.7.4)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ bundler (>= 1.12)
21
+ loginx!
22
+ rake (>= 10.0)
23
+
24
+ BUNDLED WITH
25
+ 1.12.5
@@ -0,0 +1,56 @@
1
+ # Loginx
2
+
3
+ ##install
4
+ ```
5
+ gem install loginx
6
+ ```
7
+
8
+ ##describe
9
+ ```
10
+ login server quickly without entering password
11
+ help generate rsakey
12
+ help send publickey
13
+ ```
14
+
15
+ ```
16
+ all configurations are in yaml files and you can directly edit those config files
17
+ config dir
18
+ ~/.loginx/config/loginx.conf
19
+ save user and port
20
+ ~/.loginx/projects
21
+ save server infomation
22
+ ```
23
+ ###usage
24
+ ```
25
+ loginx [project_name][server_alias]|[ip]
26
+
27
+ recommend configuring loginx alias---lx
28
+
29
+ ```
30
+ ###argvs
31
+ ```
32
+ -L list all projects
33
+ -l [project_name] list all hosts in the project
34
+ -u [username] define user default root
35
+ -a [project_name][server_alias][ip] add a record default project is default
36
+ -d [project_name][server_alias] deleta a record
37
+ -p [port] port default 22
38
+ -h/no argv help
39
+
40
+ ```
41
+ ###improvement
42
+ ```
43
+ add using [tab] to complete command
44
+ password should be encrypted
45
+ ```
46
+
47
+
48
+ ## Contributing
49
+
50
+ Bug reports and pull requests are welcome on GitHub at https://github.com/terryshi96/loginx. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
51
+
52
+
53
+ ## License
54
+
55
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
56
+
@@ -0,0 +1,3 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :spec
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ require 'optparse'
4
+ require 'methadone'
5
+ require 'loginx.rb'
6
+ require 'list.rb'
7
+ require 'yaml'
8
+ require 'rubygems'
9
+ require 'ruby_expect'
10
+ require 'setuser.rb'
11
+ require 'setport.rb'
12
+ require 'add.rb'
13
+ require 'delete.rb'
14
+ require 'core.rb'
15
+ require 'fileutils'
16
+
17
+
18
+
19
+
20
+
21
+ class App
22
+ include Methadone::Main
23
+ include Methadone::CLILogging
24
+ include Methadone::SH
25
+
26
+ main do |argument|
27
+
28
+ #initialize
29
+ config_path = File.expand_path("~/.loginx/config/")
30
+ project_path = File.expand_path("~/.loginx/projects/")
31
+ FileUtils.mkdir_p(config_path) unless File.exists?(config_path)
32
+ FileUtils.mkdir_p(project_path) unless File.exists?(project_path)
33
+
34
+ if File.exist? ("#{config_path}/config.yml")
35
+ else
36
+ file = File.new("#{config_path}/config.yml","w")
37
+ file << "---\n"
38
+ file << "config:\n"
39
+ file << " user: root\n"
40
+ file << " port: 22\n"
41
+ file.close
42
+ end
43
+
44
+ #input control
45
+ if options.empty? && ARGV.empty?
46
+ puts 'please use "loginx -h" for more infomation'
47
+ exit 1
48
+ end
49
+
50
+ if ARGV.count == 2
51
+ project = ARGV[0]
52
+ server_alias = ARGV[1]
53
+
54
+ core = Core_p.new(project,server_alias)
55
+ core.login
56
+
57
+ end
58
+ end
59
+
60
+
61
+
62
+ description "login quickly without entering password and support generating&&sending ssh_key\n"
63
+ arg :arguments, :optional
64
+ version Loginx::VERSION
65
+
66
+
67
+
68
+ #option -L in list.rb
69
+ on("-L","list all projects") do
70
+ options[:L] = true
71
+ list = List_p.new("default")
72
+ list.list_all
73
+ end
74
+
75
+ #option -l in list.rb
76
+ on("-l project","list servers under a project") do |project_name|
77
+ options[:l] = project_name
78
+ list = List_p.new(project_name)
79
+ list.list_project(project_name)
80
+ end
81
+
82
+ #option -a in add.rb
83
+ on("-a project server_alias ip password","add a new record") do |project_name|
84
+ options[:a] = project_name
85
+ if ARGV.count != 3
86
+ puts "you must offer server_alias ip and password"
87
+ exit 1
88
+ end
89
+ server_alias = ARGV.shift
90
+ ip = ARGV.shift
91
+ password = ARGV.shift
92
+ record = ADD.new(server_alias,ip,password)
93
+ record.add_record(project_name)
94
+ end
95
+
96
+ #option -d in delete.rb
97
+ on("-d project server_alias","delete a record") do |project_name|
98
+ options[:d] = project_name
99
+ if ARGV.count != 1 && ARGV.count != 0
100
+ puts "need project and server alias or just project"
101
+ exit 1
102
+ end
103
+ if ARGV.empty?
104
+ record = Delete_p.new
105
+ record.del_file(project_name)
106
+ exit 0
107
+ end
108
+ server_alias = ARGV.shift
109
+ record = Delete_p.new(server_alias)
110
+ record.del_record(project_name)
111
+ end
112
+
113
+ #option -u in Setuser.rb
114
+ on("-u user","set user (default root)") do |user|
115
+ options[:u] = user
116
+ set = Setuser.new(options[:u])
117
+ set.update
118
+
119
+ end
120
+
121
+ #option -p in Setport.rb
122
+ on("-p Port","set port (default 22)") do |value|
123
+ options[:p] = value
124
+ set = Setport.new(options[:p])
125
+ set.update
126
+ end
127
+
128
+ go!
129
+
130
+ end
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+ class ADD
3
+ attr_accessor :server_alias, :ip, :password
4
+
5
+
6
+ def initialize(server_alias,ip,password)
7
+ self.server_alias = server_alias
8
+ self.ip = ip
9
+ self.password = password
10
+ end
11
+
12
+ def add_record(value)
13
+ project_path = File.expand_path("~/.loginx/projects/")
14
+ if Loginx::Exist.project_exist?(value)
15
+ @info = {}
16
+ detail = {}
17
+ detail['ip'] = self.ip
18
+ detail['password'] = self.password
19
+ @info.store(self.server_alias,detail)
20
+ @load = YAML::load(File.open("#{project_path}/#{value}.yml"))
21
+ if @load.has_key?(self.server_alias)
22
+ puts "the server alias already exists,will you overwrite it(y/n)"
23
+ gets
24
+ unless $_.chomp =='y'
25
+ puts "nothing changed"
26
+ exit 1
27
+ end
28
+ end
29
+ @load.merge!(@info)
30
+
31
+ else
32
+ puts "project #{value} does not exist , will you create it(y/n)"
33
+ gets
34
+ if $_.chomp == 'y'
35
+ file = File.new("#{project_path}/#{value}.yml","w")
36
+ file << "---\n"
37
+ file << "example:\n"
38
+ file << " ip: nil\n"
39
+ file << " password: nil\n"
40
+ file.close
41
+ add_record(value)
42
+ else
43
+ puts "nothing changed"
44
+ exit 1
45
+ end
46
+ end
47
+
48
+
49
+ File.open("#{project_path}/#{value}.yml","w") do |file|
50
+ YAML.dump(@load,file)
51
+ file.close
52
+ #file.write @info.to_yaml
53
+ puts "update successfully"
54
+ end
55
+
56
+
57
+ end
58
+
59
+ end
60
+
61
+
62
+
@@ -0,0 +1,88 @@
1
+ # coding: utf-8
2
+ class Core_p
3
+ attr_accessor :project , :server_alias
4
+ $public_key_path = File.expand_path("~/.ssh/id_rsa.pub")
5
+ $config_path = File.expand_path("~/.loginx/config/config.yml")
6
+ $project_path = File.expand_path("~/.loginx/projects/")
7
+
8
+ def initialize(project,server_alias)
9
+ self.project = project
10
+ self.server_alias = server_alias
11
+ if !File.exist?("#{$public_key_path}")
12
+ puts "you dont have a pair of ssh key,we will generate it"
13
+ self.generate_key
14
+ end
15
+ end
16
+
17
+ #when trying to send public key
18
+ def generate_key
19
+ puts "please enter return 3 times"
20
+ cmd = "ssh-keygen -t rsa"
21
+ `#{cmd}`
22
+ end
23
+
24
+ def login
25
+ if Loginx::Exist.project_exist?(self.project)
26
+ config = YAML::load (File.open("#{$config_path}"))
27
+ info = YAML::load(File.open("#{$project_path}/#{self.project}.yml"))
28
+ if !info.has_key?(self.server_alias)
29
+ puts "sorry the server alias does not exist"
30
+ exit 1
31
+ end
32
+ user = config['config']['user']
33
+ port = config['config']['port']
34
+ ip = info[self.server_alias]['ip']
35
+ password = info[self.server_alias]['password']
36
+ cmd = "ssh -p #{port} #{user}@#{ip}"
37
+ exp = RubyExpect::Expect.spawn("#{cmd}")
38
+ exp.procedure do
39
+ any do
40
+ expect /Permission denied/ do
41
+ puts "this user is not allowed"
42
+ end
43
+
44
+ expect /continue connecting/ do
45
+ send "yes"
46
+ end
47
+
48
+ expect /\$\s+$/ do
49
+ puts "login successfully and press return to continue"
50
+ interact
51
+ end
52
+
53
+ expect /assword/ do
54
+ cmd = "ssh-copy-id -i #{user}@#{ip}"
55
+ exp2 = RubyExpect::Expect.spawn("#{cmd}")
56
+ exp2.procedure do
57
+ any do
58
+ expect /Permission denied/ do
59
+ puts "this user is not allowed"
60
+ end
61
+
62
+ expect /continue connecting/ do
63
+ send "yes"
64
+ end
65
+
66
+ expect /assword/ do
67
+ send "#{password}"
68
+ sleep 3
69
+ puts "send ssh key successfully, now you can try again, or use |ssh #{user}@#{ip}|"
70
+ exit 0
71
+ end
72
+
73
+ run
74
+ end
75
+ end
76
+ end
77
+
78
+ run
79
+ end
80
+ end
81
+ else
82
+ puts "project does not exist"
83
+ exit 1
84
+ end
85
+ end
86
+
87
+
88
+ end
@@ -0,0 +1,69 @@
1
+ # coding: utf-8
2
+ class Delete_p
3
+ attr_accessor :server_alias
4
+
5
+ def initialize(server_alias = nil)
6
+ self.server_alias = server_alias
7
+ end
8
+
9
+ def del_record(value)
10
+ project_path = File.expand_path("~/.loginx/projects/")
11
+ if Loginx::Exist.project_exist?(value)
12
+ @load = YAML::load(File.open("#{project_path}/#{value}.yml"))
13
+ if !@load.has_key?(self.server_alias)
14
+ puts "sorry the server alias does not exist"
15
+ exit 1
16
+ end
17
+ puts "Are you sure to delete it(y/n)"
18
+ gets
19
+ if $_.chomp =='y'
20
+ @load.delete(self.server_alias)
21
+ else
22
+ puts "nothing changed"
23
+ exit 1
24
+ end
25
+ else
26
+ puts "project #{value} does not exist!"
27
+ exit 1
28
+ end
29
+
30
+
31
+ File.open("#{project_path}/#{value}.yml","w") do |file|
32
+ YAML.dump(@load,file)
33
+ file.close
34
+ #file.write @info.to_yaml
35
+ puts "update successfully"
36
+ end
37
+ end
38
+
39
+
40
+
41
+ def del_file(value)
42
+ project_path = File.expand_path("~/.loginx/projects/")
43
+
44
+ if Loginx::Exist.project_exist?(value)
45
+
46
+ puts "Are you sure to delete it(y/n)"
47
+ gets
48
+ if $_.chomp =='y'
49
+
50
+ File.delete("#{project_path}/#{value}.yml")
51
+ puts "update successfully"
52
+ else
53
+ puts "nothing changed"
54
+ exit 1
55
+ end
56
+ else
57
+ puts "project #{value} does not exist!"
58
+ exit 1
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+
65
+
66
+
67
+
68
+
69
+
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ require 'find'
3
+
4
+ class List_p
5
+ attr_accessor :project
6
+
7
+
8
+ def initialize(value)
9
+ self.project = value
10
+ end
11
+
12
+ def list_all
13
+ project_path = File.expand_path("~/.loginx/projects/")
14
+ Find.find("#{project_path}") do |filename|
15
+ puts File.basename(filename,'.yml')
16
+ end
17
+ end
18
+
19
+ #find project and list records
20
+ def list_project(value)
21
+ if Loginx::Exist.project_exist?(value)
22
+ project_path = File.expand_path("~/.loginx/projects/")
23
+ projects = YAML::load (File.open("#{project_path}/#{value}.yml"))
24
+ if projects == nil
25
+ puts "no servers in this project"
26
+ exit 1
27
+ end
28
+ projects.each do |item|
29
+ puts item
30
+ end
31
+ else
32
+ puts "cant find this project"
33
+ exit 1
34
+ end
35
+ end
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ require "loginx/version"
3
+
4
+ module Loginx
5
+ # Your code goes here...
6
+ class Exist
7
+
8
+ def self.project_exist?(value)
9
+ project_path = File.expand_path("~/.loginx/projects/")
10
+ @flag = 0
11
+
12
+ if File.exist?("#{project_path}")
13
+ Find.find("#{project_path}") do |filename|
14
+ if File.basename(filename,'.yml') == value
15
+ @flag = 1
16
+ return true
17
+ end
18
+ end
19
+
20
+ if @flag == 0
21
+ return false
22
+ end
23
+
24
+ else
25
+ puts "initialize successfully,please try again"
26
+ end
27
+
28
+ end
29
+
30
+
31
+ end
32
+ end
@@ -0,0 +1,4 @@
1
+ # coding: utf-8
2
+ module Loginx
3
+ VERSION = "1.0.0"
4
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ class Setport
3
+ attr_accessor :port
4
+
5
+ #read from config file
6
+ def initialize(value)
7
+ self.port = value
8
+ end
9
+
10
+ #update value
11
+ def update
12
+ config_path = File.expand_path("~/.loginx/config/config.yml")
13
+ if File.exist?("#{config_path}")
14
+ config = YAML::load (File.open("#{config_path}"))
15
+ config['config']['port'] = self.port
16
+ File.open("#{config_path}","w") do |file|
17
+ file.write config.to_yaml
18
+ file.close
19
+ puts "update successfully,now default user is #{self.port}"
20
+ end
21
+ else
22
+ puts "initialize successfully please try again"
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ class Setuser
3
+ attr_accessor :user
4
+
5
+ #read from config file
6
+ def initialize(value)
7
+ self.user = value
8
+ end
9
+
10
+ #update value
11
+ def update
12
+ config_path = File.expand_path("~/.loginx/config/config.yml")
13
+ if File.exist?("#{config_path}")
14
+ config = YAML::load (File.open("#{config_path}"))
15
+ config['config']['user'] = self.user
16
+ File.open("#{config_path}","w") do |file|
17
+ file.write config.to_yaml
18
+ file.close
19
+ puts "update successfully,now default user is #{self.user}"
20
+ end
21
+ else
22
+ puts "initialize successfully please try again"
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'loginx/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "loginx"
8
+ spec.version = Loginx::VERSION
9
+ spec.authors = ["terryshi"]
10
+ spec.email = ["terryshi96@yahoo.com"]
11
+
12
+ spec.summary = %q{quick login}
13
+ spec.description = %q{quick login}
14
+ spec.homepage = "https://github.com/terryshi96/loginx"
15
+ spec.license = ""
16
+
17
+
18
+
19
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ spec.bindir = "bin"
21
+ spec.executables = ["loginx"]
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", ">= 1.12"
25
+ spec.add_development_dependency "rake", ">= 10.0"
26
+ spec.add_dependency('methadone', '>= 1.9.3')
27
+ spec.add_dependency('ruby_expect')
28
+
29
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: loginx
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - terryshi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: methadone
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 1.9.3
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 1.9.3
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby_expect
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
+ description: quick login
70
+ email:
71
+ - terryshi96@yahoo.com
72
+ executables:
73
+ - loginx
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - Gemfile.lock
81
+ - README.md
82
+ - Rakefile
83
+ - bin/loginx
84
+ - lib/add.rb
85
+ - lib/core.rb
86
+ - lib/delete.rb
87
+ - lib/list.rb
88
+ - lib/loginx.rb
89
+ - lib/loginx/version.rb
90
+ - lib/setport.rb
91
+ - lib/setuser.rb
92
+ - loginx.gemspec
93
+ homepage: https://github.com/terryshi96/loginx
94
+ licenses:
95
+ - ''
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.6.6
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: quick login
117
+ test_files: []