netsoul 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1fca4fc8cb31539dcadae7a5435367ccf62b30d8
4
+ data.tar.gz: 39ae71d45700d0a145d35a0c530471753e104e97
5
+ SHA512:
6
+ metadata.gz: d4b8ef7653cb49dba99db630aded565bfce9c4eb4aeca92e02ea74ffd112979f913bfd3be2e7ab8ef1d2f57772e8873514b912ca2c4eb7af264270a0c1b4814f
7
+ data.tar.gz: 44c2dfa0b942484e5d4c079d3b46bd5fe1614c36eb4d0f554aefc12eeee9d6de5e7df0360583dbfeed6e4977a370a1c9dcb1248391e68b21bdd562290c80612a
data/.gitignore ADDED
@@ -0,0 +1,42 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /vendor/bundle
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ # .ruby-version
32
+ # .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
36
+
37
+ /.yardoc
38
+ /Gemfile.lock
39
+ /.idea/
40
+ /lib/*.so
41
+ /lib/*.dll
42
+ /lib/*.dylib
data/.pullreview.yml ADDED
@@ -0,0 +1,77 @@
1
+ ---
2
+ rules:
3
+ ignore:
4
+ # - add_db_index
5
+ # - add_underscores_to_large_numeric
6
+ # - always_else_in_case
7
+ # - ambiguous_first_argument
8
+ # - assigned_but_unused_variable
9
+ # - assignment_in_conditional
10
+ # - avoid_copy_paste
11
+ # - avoid_empty_rescue_block
12
+ # - avoid_global_variable
13
+ # - avoid_similar_code
14
+ # - avoid_singleton_variable
15
+ # - avoid_using_curly_braces_for_multi-line_blocks
16
+ # - check_return_value_of_save
17
+ # - don_t_use_parentheses_around_the_condition
18
+ # - extra_blank_line_detected
19
+ # - extra_blank_line_detected_at_body_beginning
20
+ # - extra_blank_line_detected_at_body_end
21
+ # - favor_if_unless_for_single_line
22
+ # - improve_coverage_of_class
23
+ # - improve_coverage_of_method
24
+ # - improve_coverage_of_project
25
+ # - indent_when_as_deep_as_case
26
+ - missing_class_documentation
27
+ - missing_method_documentation
28
+ # - naming_convention_for_class
29
+ # - naming_convention_for_method
30
+ # - naming_convention_for_module
31
+ # - no_for_loop
32
+ # - no_instance_variable_in_partial
33
+ # - no_params_in_view
34
+ # - no_session_in_view
35
+ # - omit_parentheses_when_no_args
36
+ # - prefer_accolades_over_doend_for_single-line_blocks
37
+ # - prefer_map_over_collect
38
+ # - prefer_reduce_over_inject
39
+ # - prefer_ruby_19_new_hash_syntax
40
+ # - prefer_ruby_19_new_lambda_syntax
41
+ # - prefer_single_quoted_strings
42
+ - reduce_number_of_params
43
+ # - refactor_complex_class
44
+ # - refactor_complex_method
45
+ # - remove_trailing_whitespace
46
+ # - restrict_auto_generated_routes
47
+ # - shadowing_outer_local_variable
48
+ # - simplify_call_to_render
49
+ # - simplify_call_to_render_partial
50
+ # - space_between_curly_brace_and_pipe_missing
51
+ # - space_inside_closing_curly_brace_missing
52
+ # - space_inside_opening_curly_brace_missing
53
+ # - space_inside_parentheses_detected
54
+ # - space_inside_square_brackets_detected
55
+ # - space_missing_after_colon
56
+ # - space_missing_after_comma
57
+ # - space_missing_after_hash_sign
58
+ # - space_missing_after_semicolon
59
+ # - space_missing_inside_closing_curly_brace
60
+ # - space_missing_inside_opening_curly_brace
61
+ # - space_missing_to_left_of_parentheses
62
+ # - star_interpreted_as_argument_prefix
63
+ # - surrounding_space_missing_for_accolade
64
+ # - surrounding_space_missing_for_addition
65
+ # - surrounding_space_missing_for_arrow
66
+ # - surrounding_space_missing_for_assign
67
+ # - surrounding_space_missing_for_divide
68
+ # - surrounding_space_missing_for_equality
69
+ # - surrounding_space_missing_for_greater_than
70
+ # - surrounding_space_missing_for_incr
71
+ # - surrounding_space_missing_for_minus
72
+ # - surrounding_space_missing_for_multiplication
73
+ # - surrounding_space_missing_for_not_equal
74
+ # - surrounding_space_missing_for_t_square
75
+ # - use_a_logger
76
+ # - use_def_with_parentheses_when_there_are_arguments
77
+ # - uses_spaces_instead_of_tabs
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,24 @@
1
+ # Reference here: https://github.com/bbatsov/rubocop/blob/master/config/enabled.yml
2
+
3
+ Metrics/LineLength:
4
+ Description: 'Limit lines to 165 characters.'
5
+ Enabled: true
6
+ Max: 165
7
+
8
+ Metrics/MethodLength:
9
+ Description: 'Try to avoid methods longer than 50 lines of code.'
10
+ Enabled: true
11
+ Max: 50
12
+
13
+ Metrics/ClassLength:
14
+ Description: 'Try to avoid methods longer than 50 lines of code.'
15
+ Enabled: true
16
+ Max: 150
17
+
18
+ Style/Documentation:
19
+ Description: 'Document classes and non-namespace modules.'
20
+ Enabled: false
21
+
22
+ AllCops:
23
+ Excludes:
24
+ - 'bin/**'
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.6
5
+ - 2.2.3
6
+ script: bundle exec rspec spec
data/.versions.conf ADDED
@@ -0,0 +1,2 @@
1
+ ruby=2.2.3
2
+ ruby-gemset=netsoul
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in netsoul.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Christian Kakesa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Netsoul-Ruby (formerly libnetsoul-rb) [![Gem Version](https://badge.fury.io/rb/netsoul-ruby.svg)](http://badge.fury.io/rb/netsoul-ruby) [![Build Status](https://travis-ci.org/fenicks/netsoul-ruby.svg?branch=master)](https://travis-ci.org/fenicks/netsoul-ruby) [![Coverage Status](https://coveralls.io/repos/fenicks/netsoul-ruby/badge.svg?branch=master&service=github)](https://coveralls.io/github/fenicks/netsoul-ruby?branch=master)
2
+
3
+ This gem is a simple and efficient Netsoul client implementation written in Ruby.
4
+ You can use it as a Ruby gem in order to implement your own Netsoul client in Ruby or just use the provided Netsoul client.
5
+
6
+ *__[History]__: 8 years after writing my first ruby lines of code, I decide to rewrite this old own with all my Ruby backgrounds.*
7
+
8
+ ## Features
9
+
10
+ * __MD5__ authentication
11
+ * __Kerberos__ (my own Ruby native extension) authentication. Something is wrong with kerberos server or gssapi __[work in progress]__
12
+ * Data transfert is supported **only for the library part, not the provided client**, use it at your own. _My academic account was closed when I tryed files transfert at school_.
13
+ * Identification Netsoul client is provided
14
+ * Netsoul message library: all netsoul DSL is covered by the 'netsoul/message' module
15
+
16
+ ## Installation
17
+
18
+ ### On your desktop
19
+
20
+ ```bash
21
+ gem install netsoul
22
+ ```
23
+
24
+ ### In your project
25
+
26
+ ```ruby
27
+ # Gemfile
28
+ gem 'netsoul', '~> 0.1'
29
+ ```
30
+
31
+ ```ruby
32
+ # project.rb
33
+ require 'netsoul'
34
+ ```
35
+
36
+ ## Use the client
37
+
38
+ After installing the gem **netsoul**, call the client as described bellow.
39
+
40
+ ```bash
41
+ netsoul-ruby -config netsoul-config.yml
42
+ ```
43
+
44
+ ### Example of _netsoul-config.yml_ file
45
+
46
+ #### Standard (MD5) authentication
47
+
48
+ ```yaml
49
+ ---|
50
+ :login: 'kakesa_c'
51
+ :socks_password: 'my socks password'
52
+ # :unix_password: 'unix password needed for kerberos authentication' # :auth_method must be set to :krb5
53
+ # :auth_method: :std # :std, :krb5
54
+ # :server_host: 'ns-server.epita.fr'
55
+ # :server_port: 4242
56
+ # :state: :none # :actif, :away, :connection, :idle, :lock, :server, :none
57
+ # :location: 'Home'
58
+ # :user_group: 'ETNA_2008'
59
+ ```
60
+
61
+ #### Kerberos authentication
62
+
63
+ ```yaml
64
+ ---|
65
+ :login: 'kakesa_c'
66
+ :unix_password: 'unix password'
67
+ :auth_method: :krb5 # :std, :krb5
68
+ # :socks_password: 'my socks password'
69
+ # :server_host: 'ns-server.epita.fr'
70
+ # :server_port: 4242
71
+ # :state: :none # :actif, :away, :connection, :idle, :lock, :server, :none
72
+ # :location: 'Home'
73
+ # :user_group: 'ETNA_2008'
74
+ ```
75
+
76
+ ## Use the library in custom Netsoul Ruby client
77
+
78
+ __[TODO]__
79
+
80
+ ## Contributing
81
+
82
+ 1. Fork it ( https://github.com/fenicks/netsoul-ruby/fork )
83
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
84
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
85
+ 4. Push to the branch (`git push origin my-new-feature`)
86
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ task default: :spec
8
+
9
+ require 'rake/extensiontask'
10
+ Rake::ExtensionTask.new 'netsoul_kerberos' do |ext|
11
+ ext.lib_dir = 'lib'
12
+ end
13
+
14
+ desc 'Generate documentation'
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = %w(lib/**/*.rb - LICENSE.txt)
17
+ t.options = %w(--main README.md --no-private --protected)
18
+ end
data/bin/netsoul-ruby ADDED
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: binary -*-
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'base64'
7
+ require 'digest/md5'
8
+ require 'netsoul'
9
+ require 'socket'
10
+ require 'uri'
11
+
12
+ module Netsoul
13
+ class Client
14
+ include Logging
15
+
16
+ attr_reader :started
17
+
18
+ def initialize(*args)
19
+ opts = args.last.is_a?(Hash) ? args.last : {}
20
+ @config = Config.new(opts)
21
+ @started = false
22
+ end
23
+
24
+ def auth_ag
25
+ sock_send(Message.auth_ag)
26
+ fail Netsoul::IdentificationError, 'Identification failed.'.freeze unless sock_get.split(' ')[1] == '002'.freeze
27
+ end
28
+ private :auth_ag
29
+
30
+ def auth_method
31
+ if @config.auth_method == :krb5
32
+ sock_send(Message.kerberos_auth(@config))
33
+ else
34
+ sock_send(Message.standard_auth(@config))
35
+ end
36
+ fail Netsoul::AuthenticationError, 'Authentication failed.'.freeze unless sock_get.split(' ')[1] == '002'
37
+ end
38
+ private :auth_method
39
+
40
+ def auth_status
41
+ sock_send(Message.attach)
42
+ sock_send(Message.user_state(@config.state, Time.now.to_i))
43
+ end
44
+ private :auth_status
45
+
46
+ def connect
47
+ @sock = TCPSocket.new(@config.server_host, @config.server_port)
48
+ fail Netsoul::SocketError, 'Could not open a socket. Connection is unavailable.'.freeze unless @sock
49
+
50
+ _cmd, _socket_num, md5_hash, client_ip, client_port, _server_timestamp = sock_get.split
51
+ @config.build_user_connection_info md5_hash: md5_hash, client_ip: client_ip, client_port: client_port
52
+
53
+ auth_ag
54
+ auth_method
55
+ auth_status
56
+ @started = true
57
+ end
58
+
59
+ def disconnect
60
+ sock_send(Message.ns_exit)
61
+ ensure
62
+ sock_close
63
+ end
64
+
65
+ def sock_send(str)
66
+ log :info, "[send] #{str.chomp}"
67
+ @sock.puts str
68
+ end
69
+
70
+ def sock_get
71
+ res = @sock.gets
72
+ log :info, "[get ] #{res.chomp}"
73
+ res
74
+ end
75
+
76
+ def sock_close
77
+ @started = false
78
+ @sock.close
79
+ rescue
80
+ nil
81
+ end
82
+ end
83
+ end
84
+
85
+ if __FILE__ == $PROGRAM_NAME
86
+ $stderr.sync = true
87
+ require 'optparse'
88
+ require 'yaml'
89
+
90
+ options = {}
91
+ OptionParser.new do |opts|
92
+ opts.banner = 'Usage: netsoul-ruby [options]'.freeze
93
+ opts.separator "\nNetsoul-Ruby options:".freeze
94
+ opts.on('-c'.freeze, '--config FILE'.freeze, 'Configuration file in YAML'.freeze) do |file|
95
+ options[:config] = file
96
+ unless File.file?(options[:config])
97
+ puts '[ERROR] Configuration is not a valid file'
98
+ exit
99
+ end
100
+ options[:user_opts] = YAML.load_file(options[:config])
101
+ end
102
+ opts.on('-h', '--help', 'Display this screen') do
103
+ puts opts
104
+ exit
105
+ end
106
+ end.parse!
107
+
108
+ unless options.include?(:config)
109
+ puts '[ERROR] Configuration file is not provided'
110
+ exit
111
+ end
112
+
113
+ c = Netsoul::Client.new options[:user_opts]
114
+ c.connect
115
+ if c.started
116
+ loop do
117
+ res = c.sock_get
118
+ c.sock_send res if res.to_s.match(/^ping.*/)
119
+ sleep 1
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,10 @@
1
+ require 'mkmf'
2
+
3
+ have_library('krb5', 'krb5_init_context')
4
+ have_library('gssapi_krb5', 'gss_init_sec_context')
5
+ have_header('string.h')
6
+ have_header('krb5.h')
7
+ have_header('gssapi/gssapi.h')
8
+ have_header('gssapi/gssapi_krb5.h')
9
+
10
+ create_makefile('netsoul_kerberos')
@@ -0,0 +1,206 @@
1
+ /*
2
+ ** This file is part of RubySoul project.
3
+ **
4
+ ** Test for the kerberos authentication.
5
+ **
6
+ ** @author Christian KAKESA <christian.kakesa@gmail.com>
7
+ */
8
+
9
+ #include <stdio.h>
10
+ #include <stdlib.h>
11
+ #include <string.h>
12
+ #include <krb5.h>
13
+ #include <gssapi/gssapi.h>
14
+ #include <gssapi/gssapi_krb5.h>
15
+
16
+ #include "kerberos.h"
17
+
18
+ void display_status(k_data_t *data)
19
+ {
20
+ OM_uint32 minor, status;
21
+ gss_buffer_desc msg;
22
+
23
+ gss_display_status(&minor, data->min, GSS_C_GSS_CODE, GSS_C_NO_OID, &status, &msg);
24
+ if (msg.value) puts(msg.value);
25
+ }
26
+
27
+ krb5_error_code get_new_tickets( k_data_t *data,
28
+ krb5_context context,
29
+ krb5_principal principal,
30
+ krb5_ccache ccache)
31
+ {
32
+ krb5_error_code ret;
33
+ krb5_get_init_creds_opt opt;
34
+ krb5_creds cred;
35
+ // char * password = NULL;
36
+
37
+ memset(&cred, 0, sizeof(cred));
38
+ krb5_get_init_creds_opt_init (&opt);
39
+ ret = krb5_get_init_creds_password(context,
40
+ &cred,
41
+ principal,
42
+ data->unix_pass,
43
+ krb5_prompter_posix,
44
+ NULL,
45
+ 0,
46
+ NULL,
47
+ &opt);
48
+ if (ret == KRB5_LIBOS_PWDINTR ||
49
+ ret == KRB5KRB_AP_ERR_MODIFIED ||
50
+ ret == KRB5KRB_AP_ERR_MODIFIED)
51
+ return (1);
52
+ else if (ret)
53
+ return (2);
54
+ if (krb5_cc_initialize(context, ccache, cred.client))
55
+ return (3);
56
+ if (krb5_cc_store_cred(context, ccache, &cred))
57
+ return (3);
58
+ //krb5_free_creds_contents(context, &cred);
59
+ return (0);
60
+ }
61
+
62
+ int my_init(k_data_t *data)
63
+ {
64
+ krb5_error_code ret;
65
+ krb5_context context;
66
+ krb5_ccache ccache;
67
+ krb5_principal principal;
68
+
69
+ if (krb5_init_context(&context))
70
+ return (1);
71
+
72
+ if (!data->login ||
73
+ krb5_build_principal(context, &principal, sizeof(NS_REALM) - 1, NS_REALM, data->login, NULL))
74
+ return (1);
75
+ if (krb5_cc_default(context, &ccache))
76
+ return (1);
77
+
78
+ ret = get_new_tickets(data, context, principal, ccache);
79
+ krb5_cc_close(context, ccache);
80
+ krb5_free_principal(context, principal);
81
+ krb5_free_context(context);
82
+ return (ret);
83
+ }
84
+
85
+ void import_name(k_data_t *data)
86
+ {
87
+ OM_uint32 min;
88
+ OM_uint32 maj;
89
+ gss_buffer_desc buf;
90
+
91
+ buf.value = (unsigned char *) strdup(NS_SERVICE_NAME);
92
+ buf.length = strlen((const char*)buf.value) + 1;
93
+ maj = gss_import_name(&min, &buf, GSS_C_NT_HOSTBASED_SERVICE, &data->gss_name);
94
+
95
+ if (maj != GSS_S_COMPLETE)
96
+ display_status(data);
97
+ }
98
+
99
+ void init_context(k_data_t *data)
100
+ {
101
+ OM_uint32 maj;
102
+ /* gss_buffer_t itoken = GSS_C_NO_BUFFER; */
103
+ krb5_enctype etypes[] = { ENCTYPE_DES3_CBC_SHA1, ENCTYPE_NULL };
104
+ int etype_count = sizeof(etypes) / sizeof(*etypes);
105
+ gss_cred_id_t credh;
106
+
107
+ maj = gss_acquire_cred( &data->min,
108
+ GSS_C_NO_NAME,
109
+ GSS_C_INDEFINITE,
110
+ GSS_C_NO_OID_SET,
111
+ GSS_C_INITIATE,
112
+ &credh,
113
+ NULL,
114
+ NULL);
115
+ if (maj != GSS_S_COMPLETE)
116
+ {
117
+ display_status(data);
118
+ return;
119
+ }
120
+ maj = gss_krb5_set_allowable_enctypes(&data->min, credh, etype_count, etypes);
121
+ if (maj != GSS_S_COMPLETE)
122
+ {
123
+ display_status(data);
124
+ return;
125
+ }
126
+ data->ctx = GSS_C_NO_CONTEXT;
127
+ maj = gss_init_sec_context( &data->min,
128
+ credh,
129
+ &data->ctx,
130
+ data->gss_name,
131
+ GSS_C_NO_OID,
132
+ GSS_C_CONF_FLAG,
133
+ 0,
134
+ GSS_C_NO_CHANNEL_BINDINGS,
135
+ data->itoken,
136
+ NULL,
137
+ &data->otoken,
138
+ NULL,
139
+ NULL);
140
+
141
+ if (data->maj != GSS_S_COMPLETE)
142
+ display_status(data);
143
+ }
144
+
145
+ int check_tokens(k_data_t *data)
146
+ {
147
+ import_name(data);
148
+ init_context(data);
149
+
150
+ if (!data->otoken.value)
151
+ {
152
+ my_init(data);
153
+ import_name(data);
154
+ init_context(data);
155
+ }
156
+ if (data->otoken.value)
157
+ return (1);
158
+
159
+ return (0);
160
+ }
161
+
162
+ /**
163
+ * Encode string in base64
164
+ */
165
+ unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len)
166
+ {
167
+ unsigned char *out, *pos;
168
+ const unsigned char *end, *in;
169
+ size_t olen;
170
+ int line_len;
171
+ const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
172
+
173
+ olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
174
+ out = malloc(olen);
175
+ if (out == NULL)
176
+ return NULL;
177
+
178
+ end = src + len;
179
+ in = src;
180
+ pos = out;
181
+ while (end - in >= 3) {
182
+ *pos++ = base64_table[in[0] >> 2];
183
+ *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
184
+ *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
185
+ *pos++ = base64_table[in[2] & 0x3f];
186
+ in += 3;
187
+ }
188
+
189
+ if (end - in) {
190
+ *pos++ = base64_table[in[0] >> 2];
191
+ if (end - in == 1) {
192
+ *pos++ = base64_table[(in[0] & 0x03) << 4];
193
+ *pos++ = '=';
194
+ } else {
195
+ *pos++ = base64_table[((in[0] & 0x03) << 4) |
196
+ (in[1] >> 4)];
197
+ *pos++ = base64_table[(in[1] & 0x0f) << 2];
198
+ }
199
+ *pos++ = '=';
200
+ line_len += 4;
201
+ }
202
+
203
+ if (out_len)
204
+ *out_len = pos - out;
205
+ return out;
206
+ }
@@ -0,0 +1,58 @@
1
+ /*
2
+ ** This file is part of RubySoul project.
3
+ **
4
+ ** Test for the kerberos authentication.
5
+ **
6
+ ** @author Christian KAKESA <christian.kakesa@gmail.com>
7
+ */
8
+
9
+ #ifndef __KERBEROS_H_
10
+ #define __KERBEROS_H_
11
+
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #include <krb5.h>
16
+ #include <gssapi/gssapi.h>
17
+ #include <gssapi/gssapi_krb5.h>
18
+
19
+ #define NS_SERVICE_NAME "host@ns-server.epitech.net"
20
+ #define NS_REALM "EPITECH.NET"
21
+
22
+ typedef struct k_data
23
+ {
24
+ char* login;
25
+ char* unix_pass;
26
+ OM_uint32 min;
27
+ OM_uint32 maj;
28
+ gss_name_t gss_name;
29
+ gss_ctx_id_t ctx;
30
+ gss_buffer_t itoken;
31
+ gss_buffer_desc otoken;
32
+ } k_data_t;
33
+
34
+ void
35
+ display_status(k_data_t *data);
36
+
37
+ krb5_error_code
38
+ get_new_tickets( k_data_t *data,
39
+ krb5_context context,
40
+ krb5_principal principal,
41
+ krb5_ccache ccache);
42
+
43
+ int
44
+ my_init(k_data_t *data);
45
+
46
+ void
47
+ import_name(k_data_t *data);
48
+
49
+ void
50
+ init_context(k_data_t *data);
51
+
52
+ int
53
+ check_tokens(k_data_t *data);
54
+
55
+ unsigned char *
56
+ base64_encode(const unsigned char *src, size_t len, size_t *out_len);
57
+
58
+ #endif /* !__KERBEROS_H_ */
@@ -0,0 +1,55 @@
1
+ /*
2
+ ** This file is part of RubySoul project.
3
+ **
4
+ ** Test for the kerberos authentication.
5
+ **
6
+ ** @author Christian KAKESA <christian.kakesa@gmail.com>
7
+ */
8
+
9
+ #include <ruby.h>
10
+
11
+ #include "kerberos.h"
12
+
13
+ VALUE cNetsoulKerberos;
14
+
15
+ static VALUE k_init(VALUE self)
16
+ {
17
+ rb_define_attr(cNetsoulKerberos, "login", 1, 1);
18
+ rb_define_attr(cNetsoulKerberos, "password", 1, 1);
19
+ rb_define_attr(cNetsoulKerberos, "token", 1, 0);
20
+ rb_define_attr(cNetsoulKerberos, "token_base64", 1, 0);
21
+ return self;
22
+ }
23
+
24
+ static VALUE k_build_token(VALUE self, VALUE login, VALUE password)
25
+ {
26
+ k_data_t *data;
27
+ unsigned char *token_base64;
28
+ unsigned char *token;
29
+ size_t elen;
30
+
31
+ data = calloc(1, sizeof (k_data_t));
32
+ data->login = (char*)login;
33
+ data->unix_pass = (char*)password;
34
+ data->itoken = GSS_C_NO_BUFFER;
35
+ if (check_tokens(data) != 1)
36
+ return Qfalse;
37
+
38
+ token = (unsigned char*)strdup(data->otoken.value);
39
+ token_base64 = base64_encode((const unsigned char*)data->otoken.value, data->otoken.length, &elen);
40
+ rb_iv_set(self, "@login", login);
41
+ rb_iv_set(self, "@password", password);
42
+ rb_iv_set(self, "@token", rb_str_new2((const char*)token));
43
+ rb_iv_set(self, "@token_base64", rb_str_new2((const char*)token_base64));
44
+ free(token);
45
+ free(token_base64);
46
+ free(data);
47
+ return Qtrue;
48
+ }
49
+
50
+ void Init_netsoul_kerberos()
51
+ {
52
+ cNetsoulKerberos = rb_define_class("NetsoulKerberos", rb_cObject);
53
+ rb_define_method(cNetsoulKerberos, "initialize", k_init, 0);
54
+ rb_define_method(cNetsoulKerberos, "build_token", k_build_token, 2);
55
+ }
@@ -0,0 +1,41 @@
1
+ module Netsoul
2
+ class Config
3
+ AUTH_METHODS = [:std, :krb5].freeze
4
+ USER_STATES = [:actif, :away, :connection, :idle, :lock, :server, :none].freeze
5
+ VALID_USER_CONNECTION_INFO = [:md5_hash, :client_ip, :client_port].freeze
6
+
7
+ attr_accessor :server_host,
8
+ :server_port,
9
+ :login,
10
+ :socks_password,
11
+ :unix_password,
12
+ :auth_method,
13
+ :state,
14
+ :location,
15
+ :user_group,
16
+ :user_connection_info # Not used in configuration file
17
+
18
+ attr_reader :client_name
19
+
20
+ # rubocop:disable Metrics/AbcSize
21
+ def initialize(opts = {})
22
+ @server_host = opts.fetch(:server_host, 'ns-server.epita.fr'.freeze)
23
+ @server_port = Integer(opts.fetch(:server_port, 4242))
24
+ @login = opts.fetch(:login, 'ionis'.freeze)
25
+ @socks_password = opts.fetch(:socks_password, 'socks_password'.freeze)
26
+ @unix_password = opts.fetch(:unix_password, 'unix_password'.freeze)
27
+ @auth_method = AUTH_METHODS.include?(opts[:auth_method]) ? opts[:auth_method] : :std
28
+ @state = USER_STATES.include?(opts[:state]) ? opts[:state] : :none
29
+ @location = opts.fetch(:location, 'Home'.freeze)
30
+ @user_group = opts.fetch(:user_group, 'ETNA_2008'.freeze)
31
+ @user_connection_info = {}
32
+
33
+ @client_name = opts.fetch(:client_name, '(Netsoul-Ruby) -> { Christian Kakesa, since 2009}'.freeze)
34
+ end
35
+
36
+ def build_user_connection_info(opts = {})
37
+ return unless opts.is_a?(Hash)
38
+ opts.each { |k, v| @user_connection_info[k] = v if VALID_USER_CONNECTION_INFO.include?(k) }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,6 @@
1
+ module Netsoul
2
+ class AuthenticationError < StandardError; end
3
+ class Error < StandardError; end
4
+ class IdentificationError < StandardError; end
5
+ class SocketError < StandardError; end
6
+ end
@@ -0,0 +1,55 @@
1
+ module Netsoul
2
+ class Location
3
+ class << self
4
+ def get(ip)
5
+ locations.each do |key, val|
6
+ res = ip.match(/^#{val}/)
7
+ if res
8
+ res = "#{key}"
9
+ return res
10
+ end
11
+ end
12
+ 'ext'.freeze
13
+ end
14
+
15
+ # rubocop:disable Metrics/AbcSize
16
+ def locations
17
+ {
18
+ 'lab-cisco-mid-sr': '10.251.'.freeze,
19
+ etna: '10.245.'.freeze,
20
+ lse: '10.227.42.'.freeze,
21
+ 'sda-1': '10.227.4.'.freeze,
22
+ lab: '10.227.'.freeze,
23
+ 'lab-tcom': '10.226.7.'.freeze,
24
+ 'lab-acu': '10.226.6.'.freeze,
25
+ 'lab-console': '10.226.5.'.freeze,
26
+ 'lab-mspe': '10.226.'.freeze,
27
+ epitanim: '10.225.19.'.freeze,
28
+ epidemic: '10.225.18.'.freeze,
29
+ 'sda-2': '10.225.10.'.freeze,
30
+ cycom: '10.225.8.'.freeze,
31
+ epx: '10.225.7.'.freeze,
32
+ prologin: '10.225.6.'.freeze,
33
+ nomad: '10.225.2.'.freeze,
34
+ assos: '10.225.'.freeze,
35
+ sda: '10.224.14.'.freeze,
36
+ www: '10.223.106.'.freeze,
37
+ episport: '10.223.104.'.freeze,
38
+ epicom: '10.223.103.'.freeze,
39
+ 'bde-epita': '10.223.100.'.freeze,
40
+ omatis: '10.223.42.'.freeze,
41
+ ipsa: '10.223.15.'.freeze,
42
+ lrde: '10.223.13.'.freeze,
43
+ cvi: '10.223.7.'.freeze,
44
+ epi: '10.223.1.'.freeze,
45
+ pasteur: '10.223.'.freeze,
46
+ bocal: '10.42.42.'.freeze,
47
+ sm: '10.42.'.freeze,
48
+ vpn: '10.10.'.freeze,
49
+ adm: '10.1.'.freeze,
50
+ epita: '10.'.freeze
51
+ }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,26 @@
1
+ require 'logger'
2
+
3
+ module Netsoul
4
+ module Logging # :nodoc:
5
+ PREFIX = '[Netsoul-Ruby]'.freeze
6
+
7
+ class << self
8
+ attr_writer :logger
9
+ end
10
+
11
+ def self.logger
12
+ @logger ||= ::Logger.new(STDERR).tap do |logger|
13
+ logger.level = Logger::INFO
14
+ logger.formatter = proc do |severity, datetime, _progname, msg|
15
+ "#{severity} [#{datetime.strftime('%Y-%m-%d %H:%M:%S.%L'.freeze)}] #{msg}\n"
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def log(level, message)
23
+ Netsoul::Logging.logger.send(level.to_sym, "#{PREFIX} #{message}") if Netsoul::Logging.logger
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,143 @@
1
+ module Netsoul
2
+ class Message
3
+ class << self
4
+ def _standard_auth_string(c)
5
+ str = "#{c.user_connection_info[:md5_hash]}"
6
+ str << "-#{c.user_connection_info[:client_ip]}"
7
+ str << "/#{c.user_connection_info[:client_port]}#{c.socks_password}"
8
+ Digest::MD5.hexdigest(str)
9
+ end
10
+
11
+ def standard_auth(config)
12
+ login = config.login
13
+ client_ip = config.user_connection_info[:client_ip]
14
+ user_custom_location = config.location
15
+ location = Message.escape(Location.get(client_ip) == 'ext'.freeze ? user_custom_location : Location.get(client_ip))
16
+ client_name = Message.escape(config.client_name)
17
+ "ext_user_log #{login} #{_standard_auth_string(config)} #{client_name} #{location}"
18
+ end
19
+
20
+ def _kerberos_get
21
+ require 'netsoul_kerberos'
22
+ @netsoul_kerberos ||= NetsoulKerberos.new
23
+ rescue LoadError => e
24
+ raise Netsoul::Error, "NetsoulKerberos library not found: #{e}.".freeze
25
+ end
26
+
27
+ def _kerberos_auth_klog(config)
28
+ location = Message.escape(config.location)
29
+ user_group = Message.escape(config.user_group)
30
+ client_name = Message.escape(config.client_name)
31
+ "ext_user_klog #{_kerberos_get.token_base64.slice(0, 812)} #{Message.escape(RUBY_PLATFORM)} #{location} #{user_group} #{client_name}"
32
+ end
33
+
34
+ def kerberos_auth(config)
35
+ unless _kerberos_get.build_token(config.login, config.unix_password)
36
+ fail Netsoul::Error, 'Impossible to retrieve the kerberos token.'.freeze
37
+ end
38
+ _kerberos_auth_klog(config)
39
+ end
40
+
41
+ def auth_ag
42
+ 'auth_ag ext_user none -'.freeze
43
+ end
44
+
45
+ def send_message(user, msg)
46
+ "user_cmd msg_user #{user} msg #{Message.escape(msg.to_s)}"
47
+ end
48
+
49
+ def start_writing_to_user(user)
50
+ "user_cmd msg_user #{user} dotnetSoul_UserTyping null"
51
+ end
52
+
53
+ def stop_writing_to_user(user)
54
+ "user_cmd msg_user #{user} dotnetSoul_UserCancelledTyping null"
55
+ end
56
+
57
+ def list_users(user_list)
58
+ "list_users {#{user_list}}"
59
+ end
60
+
61
+ def who_users(user_list)
62
+ "user_cmd who {#{user_list}}"
63
+ end
64
+
65
+ def watch_users(user_list)
66
+ "user_cmd watch_log_user {#{user_list}}"
67
+ end
68
+
69
+ def attach
70
+ 'user_cmd attach'.freeze
71
+ end
72
+
73
+ def user_state(state, timestamp)
74
+ "user_cmd state #{state}:#{timestamp}"
75
+ end
76
+
77
+ def user_data(data)
78
+ "user_cmd user_data #{Message.escape(data.to_s)}"
79
+ end
80
+
81
+ def xfer(user, id, filename, size, desc)
82
+ "user_cmd msg_user #{user} desoul_ns_xfer #{Message.escape("#{id} #{filename} #{size} #{desc}")}"
83
+ end
84
+
85
+ def desoul_ns_xfer(user, id, filename, size, desc)
86
+ xfer(user, id, filename, size, desc)
87
+ end
88
+
89
+ def xfer_accept(user, id)
90
+ "user_cmd msg_user #{user} desoul_ns_xfer_accept #{id}"
91
+ end
92
+
93
+ def desoul_ns_xfer_accept(user, id)
94
+ xfer_accept(user, id)
95
+ end
96
+
97
+ def xfer_data(user, id, data)
98
+ "user_cmd msg_user #{user} desoul_ns_xfer_data #{Message.escape("#{id} #{Base64.b64encode(data.to_s, data.to_s.length)}")}"
99
+ end
100
+
101
+ def desoul_ns_xfer_data(user, id, data)
102
+ xfer_data(user, id, data)
103
+ end
104
+
105
+ def xfer_cancel(user, id)
106
+ "user_cmd msg_user #{user} desoul_ns_xfer_cancel #{id}"
107
+ end
108
+
109
+ def desoul_ns_xfer_cancel(user, id)
110
+ xfer_cancel(user, id)
111
+ end
112
+
113
+ def ping
114
+ 'pong'.freeze
115
+ end
116
+
117
+ def ns_exit
118
+ 'exit'.freeze
119
+ end
120
+
121
+ def escape(str)
122
+ str = URI.escape(str, Regexp.new("#{URI::PATTERN::ALNUM}[:graph:][:punct:][:cntrl:][:print:][:blank:]", false, 'N'.freeze))
123
+ URI.escape(str, Regexp.new("[^#{URI::PATTERN::ALNUM}]", false, 'N'.freeze))
124
+ end
125
+
126
+ def unescape(str)
127
+ URI.unescape str
128
+ end
129
+
130
+ def ltrim(str)
131
+ str.to_s.gsub(/^\s+/, ''.freeze)
132
+ end
133
+
134
+ def rtrim(str)
135
+ str.to_s.gsub(/\s+$/, ''.freeze)
136
+ end
137
+
138
+ def trim(str)
139
+ rtrim(ltrim(str.to_s))
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,3 @@
1
+ module Netsoul
2
+ VERSION = '0.1.0'.freeze
3
+ end
data/lib/netsoul.rb ADDED
@@ -0,0 +1,6 @@
1
+ require_relative 'netsoul/config'
2
+ require_relative 'netsoul/errors'
3
+ require_relative 'netsoul/location'
4
+ require_relative 'netsoul/logging'
5
+ require_relative 'netsoul/message'
6
+ require_relative 'netsoul/version'
data/netsoul.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'netsoul/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'netsoul'
8
+ spec.version = Netsoul::VERSION
9
+ spec.authors = ['Christian Kakesa']
10
+ spec.email = ['christian.kakesa@gmail.com']
11
+
12
+ spec.summary = 'Netsoul client and library for ruby.'
13
+ spec.description = spec.summary
14
+ spec.homepage = 'https://github.com/fenicks/netsoul-ruby'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ # else
22
+ # fail 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
23
+ # end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = 'bin'
27
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
28
+ spec.require_paths = %w(lib ext bin)
29
+ spec.extensions = ['ext/netsoul_kerberos/extconf.rb']
30
+
31
+ spec.add_development_dependency 'bundler', '~> 1.7', '>= 1.7.0'
32
+ spec.add_development_dependency 'rake', '~> 0'
33
+ spec.add_development_dependency 'rubocop', '~> 0'
34
+ spec.add_development_dependency 'rspec', '~> 3.3'
35
+ spec.add_development_dependency 'simplecov', '~> 0'
36
+ spec.add_development_dependency 'coveralls', '~> 0'
37
+ spec.add_development_dependency 'yard', '~> 0'
38
+ spec.add_development_dependency 'rake-compiler', '~> 0.9.5'
39
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: netsoul
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Christian Kakesa
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-04 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.7'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.7.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.7'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.7.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: rake
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rubocop
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.3'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.3'
75
+ - !ruby/object:Gem::Dependency
76
+ name: simplecov
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: coveralls
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: yard
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: rake-compiler
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.9.5
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.9.5
131
+ description: Netsoul client and library for ruby.
132
+ email:
133
+ - christian.kakesa@gmail.com
134
+ executables:
135
+ - netsoul-ruby
136
+ extensions:
137
+ - ext/netsoul_kerberos/extconf.rb
138
+ extra_rdoc_files: []
139
+ files:
140
+ - ".gitignore"
141
+ - ".pullreview.yml"
142
+ - ".rspec"
143
+ - ".rubocop.yml"
144
+ - ".travis.yml"
145
+ - ".versions.conf"
146
+ - Gemfile
147
+ - LICENSE.txt
148
+ - README.md
149
+ - Rakefile
150
+ - bin/netsoul-ruby
151
+ - ext/netsoul_kerberos/extconf.rb
152
+ - ext/netsoul_kerberos/kerberos.c
153
+ - ext/netsoul_kerberos/kerberos.h
154
+ - ext/netsoul_kerberos/netsoul_kerberos.c
155
+ - lib/netsoul.rb
156
+ - lib/netsoul/config.rb
157
+ - lib/netsoul/errors.rb
158
+ - lib/netsoul/location.rb
159
+ - lib/netsoul/logging.rb
160
+ - lib/netsoul/message.rb
161
+ - lib/netsoul/version.rb
162
+ - netsoul.gemspec
163
+ homepage: https://github.com/fenicks/netsoul-ruby
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ - ext
172
+ - bin
173
+ required_ruby_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ required_rubygems_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ requirements: []
184
+ rubyforge_project:
185
+ rubygems_version: 2.4.8
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: Netsoul client and library for ruby.
189
+ test_files: []
190
+ has_rdoc: