netsoul 0.1.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.
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: