win32-taskscheduler 1.0.10 → 1.0.12
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 +4 -4
- data/CHANGELOG.md +13 -7
- data/VERSION +1 -1
- data/examples/taskscheduler_example.rb +14 -16
- data/lib/win32/taskscheduler.rb +234 -150
- data/lib/win32/taskscheduler/version.rb +1 -1
- data/lib/win32/windows/constants.rb +11 -8
- data/lib/win32/windows/helper.rb +5 -1
- data/lib/win32/windows/sid.rb +128 -0
- data/spec/functional/win32/taskschedular_spec.rb +88 -34
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b6d6ad467e4dd3518af4776d1937f9227afbc42bf743c3f3b09480141e08e15
|
4
|
+
data.tar.gz: 2a3a9d694c22b5734e30c50fe50142af6b23e6d467c8e38e33ee48bba3a15952
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5ca12f5424d5f790107a1af5ff8838e7931d846cdece3959627c0500a34ff2bcdae1fa749f253a89f4b523065ca27b15f1e629dc11ad586a990854cfbcb2a83
|
7
|
+
data.tar.gz: c423125d42002ae09d4bc1fab148004a325e399cbdeb5bb48fb6210f1f3f5af510a4ecd9f0345fd8ec1e4291905c214d2e8d58eb261cd5b7e791d2b3c59c9c49
|
data/CHANGELOG.md
CHANGED
@@ -4,21 +4,28 @@ Note: this log contains only changes from win32-taskscheduler release 0.4.0 and
|
|
4
4
|
-- it does not contain the changes from prior releases. To view change history
|
5
5
|
prior to release 0.4.0, please visit the [source repository](https://github.com/chef/win32-taskscheduler/commits).
|
6
6
|
|
7
|
-
<!-- latest_release 1.0.
|
8
|
-
## [win32-taskscheduler-1.0.
|
7
|
+
<!-- latest_release 1.0.12 -->
|
8
|
+
## [win32-taskscheduler-1.0.12](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.12) (2018-10-11)
|
9
9
|
|
10
10
|
#### Merged Pull Requests
|
11
|
-
-
|
11
|
+
- Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
|
12
12
|
<!-- latest_release -->
|
13
13
|
|
14
|
-
<!-- release_rollup since=1.0.
|
15
|
-
### Changes since 1.0.
|
14
|
+
<!-- release_rollup since=1.0.10 -->
|
15
|
+
### Changes since 1.0.10 release
|
16
16
|
|
17
17
|
#### Merged Pull Requests
|
18
|
-
-
|
18
|
+
- Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys)) <!-- 1.0.12 -->
|
19
|
+
- Refactored configure_settings [#67](https://github.com/chef/win32-taskscheduler/pull/67) ([btm](https://github.com/btm)) <!-- 1.0.11 -->
|
19
20
|
<!-- release_rollup -->
|
20
21
|
|
21
22
|
<!-- latest_stable_release -->
|
23
|
+
## [win32-taskscheduler-1.0.10](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.10) (2018-07-24)
|
24
|
+
|
25
|
+
#### Merged Pull Requests
|
26
|
+
- Fix exists? method breaking task full path search [#62](https://github.com/chef/win32-taskscheduler/pull/62) ([Vasu1105](https://github.com/Vasu1105))
|
27
|
+
<!-- latest_stable_release -->
|
28
|
+
|
22
29
|
## [win32-taskscheduler-1.0.9](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.9) (2018-07-23)
|
23
30
|
|
24
31
|
#### Merged Pull Requests
|
@@ -29,7 +36,6 @@ prior to release 0.4.0, please visit the [source repository](https://github.com/
|
|
29
36
|
- [MSYS-827] Add functional test cases [#58](https://github.com/chef/win32-taskscheduler/pull/58) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
|
30
37
|
- Add DisallowStartIfOnBatteries and StopIfGoingOnBatteries task configs [#61](https://github.com/chef/win32-taskscheduler/pull/61) ([dheerajd-msys](https://github.com/dheerajd-msys))
|
31
38
|
- Fix priority should return unique value. [#60](https://github.com/chef/win32-taskscheduler/pull/60) ([Vasu1105](https://github.com/Vasu1105))
|
32
|
-
<!-- latest_stable_release -->
|
33
39
|
|
34
40
|
## [win32-taskscheduler-1.0.2](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.2) (2018-06-13)
|
35
41
|
- Fix for exists? method returning false for task without full path. [#43](https://github.com/chef/win32-taskscheduler/pull/43)([#Vasu1105](https://github.com/Vasu1105))
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.12
|
@@ -6,30 +6,29 @@
|
|
6
6
|
#
|
7
7
|
# Modify as you see fit.
|
8
8
|
#######################################################################
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
9
|
+
require "win32/taskscheduler"
|
10
|
+
require "fileutils"
|
11
|
+
require "pp"
|
12
12
|
include Win32
|
13
13
|
|
14
|
-
puts
|
14
|
+
puts "VERSION: " + TaskScheduler::VERSION
|
15
15
|
|
16
16
|
ts = TaskScheduler.new
|
17
17
|
|
18
18
|
trigger = {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
start_year: 2009,
|
20
|
+
start_month: 4,
|
21
|
+
start_day: 11,
|
22
|
+
start_hour: 7,
|
23
|
+
start_minute: 14,
|
24
|
+
trigger_type: TaskScheduler::DAILY,
|
25
|
+
type: { "days_interval" => 1 }
|
26
26
|
}
|
27
27
|
|
28
28
|
unless ts.enum.grep(/foo/).length > 0
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
puts "Task Added"
|
29
|
+
ts.new_work_item("foo", trigger)
|
30
|
+
ts.application_name = "notepad.exe"
|
31
|
+
puts "Task Added"
|
33
32
|
end
|
34
33
|
|
35
34
|
ts.activate("foo")
|
@@ -39,7 +38,6 @@ ts.working_directory = "C:\\"
|
|
39
38
|
puts "App name: " + ts.application_name
|
40
39
|
puts "Creator: " + ts.creator
|
41
40
|
puts "Exit code: " + ts.exit_code.to_s
|
42
|
-
puts "Flags: " + ts.flags.to_s
|
43
41
|
puts "Max run time: " + ts.max_run_time.to_s
|
44
42
|
puts "Next run time: " + ts.next_run_time.to_s
|
45
43
|
puts "Parameters: " + ts.parameters
|
data/lib/win32/taskscheduler.rb
CHANGED
@@ -329,36 +329,26 @@ module Win32
|
|
329
329
|
# Sets the +user+ and +password+ for the given task. If the user and
|
330
330
|
# password are set properly then true is returned.
|
331
331
|
# throws TypeError if password is not provided for other than system users
|
332
|
-
def set_account_information(
|
333
|
-
|
334
|
-
unless SYSTEM_USERS.include?(user.upcase)
|
335
|
-
raise TypeError unless password.is_a?(String)
|
336
|
-
end
|
332
|
+
def set_account_information(user_id, password)
|
333
|
+
check_credential_requirements(user_id, password)
|
337
334
|
check_for_active_task
|
338
|
-
|
339
335
|
@password = password
|
340
|
-
|
341
|
-
begin
|
342
|
-
@task = @root.RegisterTaskDefinition(
|
343
|
-
@task.Path,
|
344
|
-
@task.Definition,
|
345
|
-
TASK_CREATE_OR_UPDATE,
|
346
|
-
user,
|
347
|
-
password,
|
348
|
-
password ? TASK_LOGON_PASSWORD : TASK_LOGON_SERVICE_ACCOUNT
|
349
|
-
)
|
350
|
-
rescue WIN32OLERuntimeError => err
|
351
|
-
raise Error, ole_error('RegisterTaskDefinition', err)
|
352
|
-
end
|
353
|
-
|
336
|
+
register_task_definition(@task.Definition, @task.Path, user_id, password)
|
354
337
|
true
|
355
338
|
end
|
356
339
|
|
357
|
-
# Returns the user associated with the task or nil if no
|
358
|
-
#
|
340
|
+
# Returns the user or group associated with the task or nil if no one has been associated with the task yet
|
341
|
+
#
|
342
|
+
# @return [String] user or group associated with the task
|
359
343
|
#
|
360
344
|
def account_information
|
361
|
-
@task.nil?
|
345
|
+
if @task.nil?
|
346
|
+
nil
|
347
|
+
elsif !@task.Definition.Principal.UserId.empty?
|
348
|
+
@task.Definition.Principal.UserId
|
349
|
+
else
|
350
|
+
@task.Definition.Principal.GroupId
|
351
|
+
end
|
362
352
|
end
|
363
353
|
|
364
354
|
# Returns the name of the application associated with the task. If
|
@@ -391,7 +381,7 @@ module Win32
|
|
391
381
|
action.Path = app if action.Type == 0
|
392
382
|
end
|
393
383
|
|
394
|
-
|
384
|
+
register_task_definition(definition)
|
395
385
|
|
396
386
|
app
|
397
387
|
end
|
@@ -426,7 +416,7 @@ module Win32
|
|
426
416
|
action.Arguments = param if action.Type == 0
|
427
417
|
end
|
428
418
|
|
429
|
-
|
419
|
+
register_task_definition(definition)
|
430
420
|
|
431
421
|
param
|
432
422
|
end
|
@@ -459,7 +449,7 @@ module Win32
|
|
459
449
|
action.WorkingDirectory = dir if action.Type == 0
|
460
450
|
end
|
461
451
|
|
462
|
-
|
452
|
+
register_task_definition(definition)
|
463
453
|
|
464
454
|
dir
|
465
455
|
end
|
@@ -511,7 +501,7 @@ module Win32
|
|
511
501
|
definition = @task.Definition
|
512
502
|
definition.Settings.Priority = priority
|
513
503
|
|
514
|
-
|
504
|
+
register_task_definition(definition)
|
515
505
|
|
516
506
|
priority
|
517
507
|
end
|
@@ -521,16 +511,12 @@ module Win32
|
|
521
511
|
# job should run.
|
522
512
|
#
|
523
513
|
def new_work_item(task, trigger, userinfo = { user: nil, password: nil })
|
524
|
-
raise TypeError unless userinfo.is_a?(Hash)
|
525
|
-
raise TypeError unless task.is_a?(String)
|
526
|
-
raise TypeError unless trigger.is_a?(Hash)
|
514
|
+
raise TypeError unless userinfo.is_a?(Hash) && task.is_a?(String) && trigger.is_a?(Hash)
|
527
515
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
end
|
533
|
-
end
|
516
|
+
# If user ID is not given, consider it as a 'SYSTEM' user
|
517
|
+
userinfo[:user] = SERVICE_ACCOUNT_USERS.first if userinfo[:user].to_s.empty?
|
518
|
+
|
519
|
+
check_credential_requirements(userinfo[:user], userinfo[:password])
|
534
520
|
|
535
521
|
taskDefinition = @service.NewTask(0)
|
536
522
|
taskDefinition.RegistrationInfo.Description = ''
|
@@ -621,18 +607,8 @@ module Win32
|
|
621
607
|
act.Path = 'cmd'
|
622
608
|
|
623
609
|
@password = userinfo[:password]
|
624
|
-
|
625
|
-
|
626
|
-
task,
|
627
|
-
taskDefinition,
|
628
|
-
TASK_CREATE_OR_UPDATE,
|
629
|
-
userinfo[:user].nil? || userinfo[:user].empty? ? 'SYSTEM': userinfo[:user],
|
630
|
-
userinfo[:password],
|
631
|
-
userinfo[:password] ? TASK_LOGON_PASSWORD : TASK_LOGON_SERVICE_ACCOUNT
|
632
|
-
)
|
633
|
-
rescue WIN32OLERuntimeError => err
|
634
|
-
raise Error, ole_error('RegisterTaskDefinition', err)
|
635
|
-
end
|
610
|
+
|
611
|
+
register_task_definition(taskDefinition, task, userinfo[:user], userinfo[:password])
|
636
612
|
|
637
613
|
@task = @root.GetTask(task)
|
638
614
|
end
|
@@ -677,7 +653,7 @@ module Win32
|
|
677
653
|
|
678
654
|
definition = @task.Definition
|
679
655
|
definition.Triggers.Remove(index)
|
680
|
-
|
656
|
+
register_task_definition(definition)
|
681
657
|
|
682
658
|
index
|
683
659
|
end
|
@@ -860,7 +836,7 @@ module Win32
|
|
860
836
|
end
|
861
837
|
end
|
862
838
|
|
863
|
-
|
839
|
+
register_task_definition(definition)
|
864
840
|
|
865
841
|
trigger
|
866
842
|
end
|
@@ -947,7 +923,7 @@ module Win32
|
|
947
923
|
trig.Delay = "PT#{trigger[:delay_duration]||0}M"
|
948
924
|
end
|
949
925
|
|
950
|
-
|
926
|
+
register_task_definition(definition)
|
951
927
|
|
952
928
|
true
|
953
929
|
end
|
@@ -1004,7 +980,7 @@ module Win32
|
|
1004
980
|
|
1005
981
|
definition = @task.Definition
|
1006
982
|
definition.RegistrationInfo.Description = comment
|
1007
|
-
|
983
|
+
register_task_definition(definition)
|
1008
984
|
|
1009
985
|
comment
|
1010
986
|
end
|
@@ -1028,7 +1004,7 @@ module Win32
|
|
1028
1004
|
|
1029
1005
|
definition = @task.Definition
|
1030
1006
|
definition.RegistrationInfo.Author = creator
|
1031
|
-
|
1007
|
+
register_task_definition(definition)
|
1032
1008
|
|
1033
1009
|
creator
|
1034
1010
|
end
|
@@ -1059,18 +1035,6 @@ module Win32
|
|
1059
1035
|
time
|
1060
1036
|
end
|
1061
1037
|
|
1062
|
-
# Returns idle settings for current active task
|
1063
|
-
#
|
1064
|
-
def idle_settings
|
1065
|
-
check_for_active_task
|
1066
|
-
idle_settings = {}
|
1067
|
-
idle_settings[:idle_duration] = @task.Definition.Settings.IdleSettings.IdleDuration
|
1068
|
-
idle_settings[:stop_on_idle_end] = @task.Definition.Settings.IdleSettings.StopOnIdleEnd
|
1069
|
-
idle_settings[:wait_timeout] = @task.Definition.Settings.IdleSettings.WaitTimeout
|
1070
|
-
idle_settings[:restart_on_idle] = @task.Definition.Settings.IdleSettings.RestartOnIdle
|
1071
|
-
idle_settings
|
1072
|
-
end
|
1073
|
-
|
1074
1038
|
# Returns the execution time limit for current active task
|
1075
1039
|
#
|
1076
1040
|
def execution_time_limit
|
@@ -1120,89 +1084,93 @@ module Win32
|
|
1120
1084
|
|
1121
1085
|
definition = @task.Definition
|
1122
1086
|
definition.Settings.ExecutionTimeLimit = limit
|
1123
|
-
|
1087
|
+
register_task_definition(definition)
|
1124
1088
|
|
1125
1089
|
max_run_time
|
1126
1090
|
end
|
1127
1091
|
|
1128
|
-
#
|
1129
|
-
# The possible options are:
|
1130
|
-
#
|
1131
|
-
# * allow_demand_start
|
1132
|
-
# * allow_hard_terminate
|
1133
|
-
# * compatibility
|
1134
|
-
# * delete_expired_task_after
|
1135
|
-
# * disallowed_start_if_on_batteries
|
1136
|
-
# * enabled
|
1137
|
-
# * execution_time_limit (or max_run_time)
|
1138
|
-
# * hidden
|
1139
|
-
# * idle_settings
|
1140
|
-
# * network_settings
|
1141
|
-
# * priority
|
1142
|
-
# * restart_count
|
1143
|
-
# * restart_interval
|
1144
|
-
# * run_only_if_idle
|
1145
|
-
# * run_only_if_network_available
|
1146
|
-
# * start_when_available
|
1147
|
-
# * stop_if_going_on_batteries
|
1148
|
-
# * wake_to_run
|
1149
|
-
# * xml_text (or xml)
|
1092
|
+
# The Idle settings of a task
|
1150
1093
|
#
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1094
|
+
# @see https://docs.microsoft.com/en-us/windows/desktop/TaskSchd/idlesettings#properties
|
1095
|
+
#
|
1096
|
+
IdleSettings = %i[idle_duration restart_on_idle stop_on_idle_end wait_timeout]
|
1097
|
+
|
1098
|
+
# Configures tasks settings
|
1099
|
+
#
|
1100
|
+
# @param [Hash] settings_hash The settings to configure a task
|
1101
|
+
# @option settings_hash [Boolean] :allow_demand_start The subject
|
1102
|
+
# @option settings_hash [Boolean] :allow_hard_terminate
|
1103
|
+
# @option settings_hash [Boolean] :disallow_start_if_on_batteries
|
1104
|
+
# @option settings_hash [Boolean] :disallow_start_on_remote_app_session
|
1105
|
+
# @option settings_hash [Boolean] :enabled
|
1106
|
+
# @option settings_hash [Boolean] :hidden
|
1107
|
+
# @option settings_hash [Boolean] :run_only_if_idle
|
1108
|
+
# @option settings_hash [Boolean] :run_only_if_network_available
|
1109
|
+
# @option settings_hash [Boolean] :start_when_available
|
1110
|
+
# @option settings_hash [Boolean] :stop_if_going_on_batteries
|
1111
|
+
# @option settings_hash [Boolean] :use_unified_scheduling_engine
|
1112
|
+
# @option settings_hash [Boolean] :volatile
|
1113
|
+
# @option settings_hash [Boolean] :wake_to_run
|
1114
|
+
# @option settings_hash [Boolean] :restart_on_idle The Idle Setting
|
1115
|
+
# @option settings_hash [Boolean] :stop_on_idle_end The Idle Setting
|
1116
|
+
# @option settings_hash [Integer] :compatibility
|
1117
|
+
# @option settings_hash [Integer] :multiple_instances
|
1118
|
+
# @option settings_hash [Integer] :priority
|
1119
|
+
# @option settings_hash [Integer] :restart_count
|
1120
|
+
# @option settings_hash [String] :delete_expired_task_after
|
1121
|
+
# @option settings_hash [String] :execution_time_limit
|
1122
|
+
# @option settings_hash [String] :restart_interval
|
1123
|
+
# @option settings_hash [String] :idle_duration The Idle Setting
|
1124
|
+
# @option settings_hash [String] :wait_timeout The Idle Setting
|
1125
|
+
#
|
1126
|
+
# @return [Hash] User input
|
1127
|
+
#
|
1128
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa383480(v=vs.85).aspx#properties
|
1129
|
+
#
|
1130
|
+
def configure_settings(settings_hash)
|
1131
|
+
raise TypeError, "User input settings are required in hash" unless settings_hash.is_a?(Hash)
|
1154
1132
|
|
1133
|
+
check_for_active_task
|
1155
1134
|
definition = @task.Definition
|
1156
1135
|
|
1157
|
-
|
1158
|
-
|
1159
|
-
|
1160
|
-
delete_expired_task_after = hash[:delete_expired_task_after]
|
1161
|
-
disallow_start_if_on_batteries = hash[:disallow_start_if_on_batteries]
|
1162
|
-
enabled = hash[:enabled]
|
1163
|
-
execution_time_limit = "PT#{hash[:execution_time_limit] || hash[:max_run_time] || 0}M"
|
1164
|
-
hidden = hash[:hidden]
|
1165
|
-
idle_duration = "PT#{hash[:idle_duration]||0}M"
|
1166
|
-
stop_on_idle_end = hash[:stop_on_idle_end]
|
1167
|
-
wait_timeout = "PT#{hash[:wait_timeout]||0}M"
|
1168
|
-
restart_on_idle = hash[:restart_on_idle]
|
1169
|
-
network_settings = hash[:network_settings]
|
1170
|
-
priority = hash[:priority]
|
1171
|
-
restart_count = hash[:restart_count]
|
1172
|
-
restart_interval = hash[:restart_interval]
|
1173
|
-
run_only_if_idle = hash[:run_only_if_idle]
|
1174
|
-
run_only_if_network_available = hash[:run_only_if_network_available]
|
1175
|
-
start_when_available = hash[:start_when_available]
|
1176
|
-
stop_if_going_on_batteries = hash[:stop_if_going_on_batteries]
|
1177
|
-
wake_to_run = hash[:wake_to_run]
|
1178
|
-
xml_text = hash[:xml_text] || hash[:xml]
|
1136
|
+
# Check for invalid setting
|
1137
|
+
invalid_settings = settings_hash.keys - valid_settings_options
|
1138
|
+
raise TypeError, "Invalid setting passed: #{invalid_settings.join(', ')}" unless invalid_settings.empty?
|
1179
1139
|
|
1180
|
-
|
1181
|
-
|
1182
|
-
definition.Settings.Compatibility = compatibility if compatibility
|
1183
|
-
definition.Settings.DeleteExpiredTaskAfter = delete_expired_task_after if delete_expired_task_after
|
1184
|
-
definition.Settings.DisallowStartIfOnBatteries = disallow_start_if_on_batteries if !disallow_start_if_on_batteries.nil?
|
1185
|
-
definition.Settings.Enabled = enabled if enabled
|
1186
|
-
definition.Settings.ExecutionTimeLimit = execution_time_limit if execution_time_limit
|
1187
|
-
definition.Settings.Hidden = hidden if hidden
|
1188
|
-
definition.Settings.IdleSettings.IdleDuration = idle_duration if idle_duration
|
1189
|
-
definition.Settings.IdleSettings.StopOnIdleEnd = stop_on_idle_end if stop_on_idle_end
|
1190
|
-
definition.Settings.IdleSettings.WaitTimeout = wait_timeout if wait_timeout
|
1191
|
-
definition.Settings.IdleSettings.RestartOnIdle = restart_on_idle if restart_on_idle
|
1192
|
-
definition.Settings.NetworkSettings = network_settings if network_settings
|
1193
|
-
definition.Settings.Priority = priority if priority
|
1194
|
-
definition.Settings.RestartCount = restart_count if restart_count
|
1195
|
-
definition.Settings.RestartInterval = restart_interval if restart_interval
|
1196
|
-
definition.Settings.RunOnlyIfIdle = run_only_if_idle if run_only_if_idle
|
1197
|
-
definition.Settings.RunOnlyIfNetworkAvailable = run_only_if_network_available if run_only_if_network_available
|
1198
|
-
definition.Settings.StartWhenAvailable = start_when_available if start_when_available
|
1199
|
-
definition.Settings.StopIfGoingOnBatteries = stop_if_going_on_batteries if !stop_if_going_on_batteries.nil?
|
1200
|
-
definition.Settings.WakeToRun = wake_to_run if wake_to_run
|
1201
|
-
definition.Settings.XmlText = xml_text if xml_text
|
1202
|
-
|
1203
|
-
update_task_definition(definition)
|
1140
|
+
# Some modification is required in user input
|
1141
|
+
hash = settings_hash.dup
|
1204
1142
|
|
1205
|
-
|
1143
|
+
# Conversion of few settings
|
1144
|
+
hash[:execution_time_limit] = hash[:max_run_time] unless hash[:max_run_time].nil?
|
1145
|
+
%i[execution_time_limit idle_duration restart_interval wait_timeout].each do |setting|
|
1146
|
+
hash[setting] = "PT#{hash[setting]}M" unless hash[setting].nil?
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
task_settings = definition.Settings
|
1150
|
+
|
1151
|
+
# Some Idle setting needs to be configured
|
1152
|
+
if IdleSettings.any? { |setting| hash.key?(setting) }
|
1153
|
+
idle_settings = task_settings.IdleSettings
|
1154
|
+
IdleSettings.each do |setting|
|
1155
|
+
unless hash[setting].nil?
|
1156
|
+
idle_settings.setproperty(camelize(setting.to_s), hash[setting])
|
1157
|
+
# This setting is not required to be configured now
|
1158
|
+
hash.delete(setting)
|
1159
|
+
end
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
# XML settings are not to be configured
|
1164
|
+
%i[xml_text xml].map { |x| hash.delete(x) }
|
1165
|
+
|
1166
|
+
hash.each do |setting, value|
|
1167
|
+
setting = camelize(setting.to_s)
|
1168
|
+
definition.Settings.setproperty(setting, value)
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
register_task_definition(definition)
|
1172
|
+
|
1173
|
+
settings_hash
|
1206
1174
|
end
|
1207
1175
|
|
1208
1176
|
# Set registration information options. The possible options are:
|
@@ -1247,7 +1215,7 @@ module Win32
|
|
1247
1215
|
definition.RegistrationInfo.Version = version if version
|
1248
1216
|
definition.RegistrationInfo.XmlText = xml_text if xml_text
|
1249
1217
|
|
1250
|
-
|
1218
|
+
register_task_definition(definition)
|
1251
1219
|
|
1252
1220
|
hash
|
1253
1221
|
end
|
@@ -1266,7 +1234,7 @@ module Win32
|
|
1266
1234
|
definition.Principal.LogonType = principals[:logon_type] if principals[:logon_type].to_s != ""
|
1267
1235
|
definition.Principal.GroupId = principals[:group_id] if principals[:group_id].to_s != ""
|
1268
1236
|
definition.Principal.RunLevel = principals[:run_level] if principals[:run_level].to_s != ""
|
1269
|
-
|
1237
|
+
register_task_definition(definition)
|
1270
1238
|
principals
|
1271
1239
|
end
|
1272
1240
|
|
@@ -1314,8 +1282,17 @@ module Win32
|
|
1314
1282
|
symbolize_keys(settings_hash)
|
1315
1283
|
end
|
1316
1284
|
|
1285
|
+
# Returns the user or group associated with the task. If no one is associated, it returns the default user i.e., 'SYSTEM'
|
1286
|
+
#
|
1287
|
+
# @param [WIN32OLE] definition
|
1288
|
+
#
|
1289
|
+
# @return [String] user_id
|
1290
|
+
#
|
1317
1291
|
def task_user_id(definition)
|
1318
|
-
definition.Principal.UserId
|
1292
|
+
user_id = definition.Principal.UserId.to_s
|
1293
|
+
user_id = definition.Principal.GroupId.to_s if user_id.empty?
|
1294
|
+
user_id = SERVICE_ACCOUNT_USERS.first if user_id.empty?
|
1295
|
+
user_id
|
1319
1296
|
end
|
1320
1297
|
|
1321
1298
|
private
|
@@ -1325,6 +1302,16 @@ module Win32
|
|
1325
1302
|
string.gsub(/([a-z\d])([A-Z])/, '\1_\2'.freeze).downcase
|
1326
1303
|
end
|
1327
1304
|
|
1305
|
+
# Converts a snake-case string to camel-case format
|
1306
|
+
#
|
1307
|
+
# @param [String] str
|
1308
|
+
#
|
1309
|
+
# @return [String] In camel case format
|
1310
|
+
#
|
1311
|
+
def camelize(str)
|
1312
|
+
str.split('_').map(&:capitalize).join
|
1313
|
+
end
|
1314
|
+
|
1328
1315
|
# Converts all the keys of a hash to underscored-symbol format
|
1329
1316
|
def symbolize_keys(hash)
|
1330
1317
|
hash.each_with_object({}) do |(k, v), h|
|
@@ -1344,22 +1331,119 @@ module Win32
|
|
1344
1331
|
}
|
1345
1332
|
end
|
1346
1333
|
|
1334
|
+
# Configurable settings options
|
1335
|
+
#
|
1336
|
+
# @note Logically, this is summation of
|
1337
|
+
# * Settings
|
1338
|
+
# * IdleSettings - [:idle_settings]
|
1339
|
+
# * :max_run_time, :xml
|
1340
|
+
#
|
1341
|
+
# @return [Array]
|
1342
|
+
#
|
1343
|
+
def valid_settings_options
|
1344
|
+
%i[allow_demand_start allow_hard_terminate compatibility delete_expired_task_after
|
1345
|
+
disallow_start_if_on_batteries disallow_start_on_remote_app_session enabled
|
1346
|
+
execution_time_limit hidden idle_duration maintenance_settings max_run_time
|
1347
|
+
multiple_instances network_settings priority restart_count restart_interval
|
1348
|
+
restart_on_idle run_only_if_idle run_only_if_network_available
|
1349
|
+
start_when_available stop_if_going_on_batteries stop_on_idle_end
|
1350
|
+
use_unified_scheduling_engine volatile wait_timeout wake_to_run xml xml_text]
|
1351
|
+
end
|
1352
|
+
|
1347
1353
|
def check_for_active_task
|
1348
1354
|
raise Error, 'No currently active task' if @task.nil?
|
1349
1355
|
end
|
1350
1356
|
|
1351
|
-
|
1352
|
-
|
1357
|
+
# Checks if the user belongs to service accounts category
|
1358
|
+
#
|
1359
|
+
# @return [Boolean] True or False
|
1360
|
+
#
|
1361
|
+
def service_account_user?(user)
|
1362
|
+
SERVICE_ACCOUNT_USERS.include?(user.to_s.upcase)
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
# Checks if the user belongs to group accounts category
|
1366
|
+
#
|
1367
|
+
# @return [Boolean] True or False
|
1368
|
+
#
|
1369
|
+
def group_user?(user)
|
1370
|
+
BUILT_IN_GROUPS.include?(user.to_s.upcase)
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
# Checks if the user belongs to system users category
|
1374
|
+
#
|
1375
|
+
# @return [Boolean] True or False
|
1376
|
+
#
|
1377
|
+
def system_user?(user)
|
1378
|
+
SYSTEM_USERS.include?(user.to_s.upcase)
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
# Checks whether the given set of user_id and password suits our requirements.
|
1382
|
+
#
|
1383
|
+
# Raises the error in case of any failures
|
1384
|
+
#
|
1385
|
+
# Password should be nil for System Users. For other users, it is required.
|
1386
|
+
#
|
1387
|
+
# @param [String] user_id
|
1388
|
+
# @param [String] password
|
1389
|
+
#
|
1390
|
+
def check_credential_requirements(user_id, password)
|
1391
|
+
user_id = user_id.to_s
|
1392
|
+
password = password.to_s
|
1393
|
+
# Password will be required for non-system users
|
1394
|
+
if password.empty?
|
1395
|
+
unless system_user?(user_id)
|
1396
|
+
raise Error, 'Password is required for non-system users'
|
1397
|
+
end
|
1398
|
+
else
|
1399
|
+
if system_user?(user_id)
|
1400
|
+
raise Error, 'Password is not required for system users'
|
1401
|
+
end
|
1402
|
+
end
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
# Returns the applicable flag as per the given users and groups which is used while
|
1406
|
+
# RegisterTaskDefinition
|
1407
|
+
#
|
1408
|
+
# @param [String] user_id
|
1409
|
+
# @param [String] password
|
1410
|
+
#
|
1411
|
+
# @return [Integer] Logon Types
|
1412
|
+
#
|
1413
|
+
def logon_type(user_id, password)
|
1414
|
+
if service_account_user?(user_id)
|
1415
|
+
TASK_LOGON_SERVICE_ACCOUNT
|
1416
|
+
elsif group_user?(user_id)
|
1417
|
+
TASK_LOGON_GROUP
|
1418
|
+
elsif !password.to_s.empty? # Password is present
|
1419
|
+
TASK_LOGON_PASSWORD
|
1420
|
+
else
|
1421
|
+
TASK_LOGON_INTERACTIVE_TOKEN
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
# Uses RegisterTaskDefinition and creates or updates the task
|
1426
|
+
#
|
1427
|
+
# @param [WIN32OLE] definition
|
1428
|
+
# @param [String] path
|
1429
|
+
# @param [String] user_id
|
1430
|
+
# @param [String] password
|
1431
|
+
#
|
1432
|
+
# @return [Integer] Logon Types
|
1433
|
+
#
|
1434
|
+
def register_task_definition(definition, path = nil, user_id = nil, password = nil)
|
1435
|
+
user_id ||= task_user_id(definition)
|
1436
|
+
path ||= @task.Path
|
1353
1437
|
@task = @root.RegisterTaskDefinition(
|
1354
|
-
|
1355
|
-
definition,
|
1356
|
-
TASK_CREATE_OR_UPDATE,
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1438
|
+
path, # Path (name) of the task
|
1439
|
+
definition, # definition of the task
|
1440
|
+
TASK_CREATE_OR_UPDATE, # Equivalent to TASK_CREATE | TASK_UPDATE
|
1441
|
+
user_id,
|
1442
|
+
password,
|
1443
|
+
logon_type(user_id, password)
|
1360
1444
|
)
|
1361
1445
|
rescue WIN32OLERuntimeError => err
|
1362
|
-
method_name = caller_locations(1,1)[0].label
|
1446
|
+
method_name = caller_locations(1, 1)[0].label
|
1363
1447
|
raise Error, ole_error(method_name, err)
|
1364
1448
|
end
|
1365
1449
|
end
|
@@ -1,7 +1,11 @@
|
|
1
|
+
require_relative 'sid'
|
1
2
|
module Windows
|
2
3
|
module TaskSchedulerConstants
|
4
|
+
SERVICE_ACCOUNT_USERS = SID::SERVICE_ACCOUNT_USERS
|
3
5
|
|
4
|
-
|
6
|
+
BUILT_IN_GROUPS = SID::BUILT_IN_GROUPS
|
7
|
+
|
8
|
+
SYSTEM_USERS = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
|
5
9
|
|
6
10
|
# Triggers
|
7
11
|
|
@@ -192,7 +196,6 @@ module Windows
|
|
192
196
|
# Not in use; currently identical to TASK_LOGON_PASSWORD
|
193
197
|
TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
|
194
198
|
|
195
|
-
|
196
199
|
TASK_MAX_RUN_TIMES = 1440
|
197
200
|
TASKS_TO_RETRIEVE = 5
|
198
201
|
|
@@ -215,12 +218,12 @@ module Windows
|
|
215
218
|
BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
|
216
219
|
IDLE_PRIORITY_CLASS = 9 # Or 10
|
217
220
|
|
218
|
-
CLSCTX_INPROC_SERVER
|
219
|
-
CLSID_CTask =
|
220
|
-
CLSID_CTaskScheduler =
|
221
|
-
IID_ITaskScheduler = [0x148BD527,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
222
|
-
IID_ITask = [0x148BD524,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
223
|
-
IID_IPersistFile = [0x0000010b,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46].pack('LSSC8')
|
221
|
+
CLSCTX_INPROC_SERVER = 0x1
|
222
|
+
CLSID_CTask = [0x148BD520, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
223
|
+
CLSID_CTaskScheduler = [0x148BD52A, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
224
|
+
IID_ITaskScheduler = [0x148BD527, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
225
|
+
IID_ITask = [0x148BD524, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
|
226
|
+
IID_IPersistFile = [0x0000010b, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSC8')
|
224
227
|
|
225
228
|
# Days of month
|
226
229
|
|
data/lib/win32/windows/helper.rb
CHANGED
@@ -8,11 +8,15 @@ module Windows
|
|
8
8
|
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
|
9
9
|
FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
|
10
10
|
|
11
|
-
ffi_lib :kernel32
|
11
|
+
ffi_lib :kernel32, :advapi32
|
12
12
|
|
13
13
|
attach_function :FormatMessage, :FormatMessageA,
|
14
14
|
[:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
|
15
15
|
|
16
|
+
attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :bool
|
17
|
+
attach_function :LookupAccountSidW, [ :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
|
18
|
+
attach_function :LocalFree, [ :pointer ], :pointer
|
19
|
+
|
16
20
|
def win_error(function, err=FFI.errno)
|
17
21
|
err_msg = ''
|
18
22
|
flags = FORMAT_MESSAGE_IGNORE_INSERTS |
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
class Pointer
|
5
|
+
def read_wstring(num_wchars = nil)
|
6
|
+
if num_wchars.nil?
|
7
|
+
# Find the length of the string
|
8
|
+
length = 0
|
9
|
+
last_char = nil
|
10
|
+
while last_char != "\000\000"
|
11
|
+
length += 1
|
12
|
+
last_char = get_bytes(0, length * 2)[-2..-1]
|
13
|
+
end
|
14
|
+
|
15
|
+
num_wchars = length
|
16
|
+
end
|
17
|
+
|
18
|
+
wide_to_utf8(get_bytes(0, num_wchars * 2))
|
19
|
+
end
|
20
|
+
|
21
|
+
def wide_to_utf8(wstring)
|
22
|
+
# ensure it is actually UTF-16LE
|
23
|
+
# Ruby likes to mark binary data as ASCII-8BIT
|
24
|
+
wstring = wstring.force_encoding('UTF-16LE')
|
25
|
+
|
26
|
+
# encode it all as UTF-8 and remove trailing CRLF and NULL characters
|
27
|
+
wstring.encode('UTF-8').strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SID
|
33
|
+
extend Windows::TaskSchedulerHelper
|
34
|
+
ERROR_INSUFFICIENT_BUFFER = 122
|
35
|
+
|
36
|
+
def self.LocalSystem
|
37
|
+
from_string_sid('S-1-5-18')
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.NtLocal
|
41
|
+
from_string_sid('S-1-5-19')
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.NtNetwork
|
45
|
+
from_string_sid('S-1-5-20')
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.BuiltinAdministrators
|
49
|
+
from_string_sid('S-1-5-32-544')
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.BuiltinUsers
|
53
|
+
from_string_sid('S-1-5-32-545')
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.Guests
|
57
|
+
from_string_sid('S-1-5-32-546')
|
58
|
+
end
|
59
|
+
|
60
|
+
# Converts a string-format security identifier (SID) into a valid, functional SID
|
61
|
+
# and returns a hash with account_name and account_simple_name
|
62
|
+
# @see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsidtosidw
|
63
|
+
#
|
64
|
+
def self.from_string_sid(string_sid)
|
65
|
+
result = FFI::MemoryPointer.new :pointer
|
66
|
+
unless ConvertStringSidToSidW(utf8_to_wide(string_sid), result)
|
67
|
+
raise FFI::LastError.error
|
68
|
+
end
|
69
|
+
result_pointer = result.read_pointer
|
70
|
+
domain, name, use = account(result_pointer)
|
71
|
+
LocalFree(result_pointer)
|
72
|
+
account_names(domain, name, use)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.utf8_to_wide(ustring)
|
76
|
+
# ensure it is actually UTF-8
|
77
|
+
# Ruby likes to mark binary data as ASCII-8BIT
|
78
|
+
ustring = (ustring + '').force_encoding('UTF-8')
|
79
|
+
|
80
|
+
# ensure we have the double-null termination Windows Wide likes
|
81
|
+
ustring += "\000\000" if ustring.empty? || ustring[-1].chr != "\000"
|
82
|
+
|
83
|
+
# encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
|
84
|
+
ustring.encode('UTF-16LE')
|
85
|
+
end
|
86
|
+
|
87
|
+
# Accepts a security identifier (SID) as input.
|
88
|
+
# It retrieves the name of the account for this SID and the name of the
|
89
|
+
# first domain on which this SID is found
|
90
|
+
# @see https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw
|
91
|
+
#
|
92
|
+
def self.account(sid)
|
93
|
+
sid = sid.pointer if sid.respond_to?(:pointer)
|
94
|
+
name_size = FFI::Buffer.new(:long).write_long(0)
|
95
|
+
referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0)
|
96
|
+
|
97
|
+
if LookupAccountSidW(nil, sid, nil, name_size, nil, referenced_domain_name_size, nil)
|
98
|
+
raise 'Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!'
|
99
|
+
elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
|
100
|
+
raise FFI::LastError.error
|
101
|
+
end
|
102
|
+
|
103
|
+
name = FFI::MemoryPointer.new :char, (name_size.read_long * 2)
|
104
|
+
referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long * 2)
|
105
|
+
use = FFI::Buffer.new(:long).write_long(0)
|
106
|
+
unless LookupAccountSidW(nil, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use)
|
107
|
+
raise FFI::LastError.error
|
108
|
+
end
|
109
|
+
[referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long]
|
110
|
+
end
|
111
|
+
|
112
|
+
# Formats domain, name and returns a hash with
|
113
|
+
# account_name and account_simple_name
|
114
|
+
#
|
115
|
+
def self.account_names(domain, name, _use)
|
116
|
+
account_name = !domain.to_s.empty? ? "#{domain}\\#{name}" : name
|
117
|
+
account_simple_name = name
|
118
|
+
{ account_name: account_name, account_simple_name: account_simple_name }
|
119
|
+
end
|
120
|
+
|
121
|
+
SERVICE_ACCOUNT_USERS = [self.LocalSystem, self.NtLocal, self.NtNetwork].map do |user|
|
122
|
+
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
123
|
+
end.flatten.freeze
|
124
|
+
|
125
|
+
BUILT_IN_GROUPS = [self.BuiltinAdministrators, self.BuiltinUsers, self.Guests].map do |user|
|
126
|
+
[user[:account_simple_name].upcase, user[:account_name].upcase]
|
127
|
+
end.flatten.freeze
|
128
|
+
end
|
@@ -53,7 +53,7 @@ RSpec.describe Win32::TaskScheduler, :windows_only do
|
|
53
53
|
expect(no_of_tasks).to eq(0)
|
54
54
|
end
|
55
55
|
|
56
|
-
it
|
56
|
+
it "Raises error when path separators(\\\) are absent" do
|
57
57
|
invalid_path = 'Foo'
|
58
58
|
expect { ts.new(@task, @trigger, invalid_path) }.to raise_error(ArgumentError)
|
59
59
|
expect(no_of_tasks).to eq(0)
|
@@ -215,9 +215,9 @@ RSpec.describe Win32::TaskScheduler, :windows_only do
|
|
215
215
|
|
216
216
|
it 'Execute(Start) the Task' do
|
217
217
|
@ts.run
|
218
|
-
expect(@ts.status).to eq('running')
|
219
|
-
|
220
|
-
|
218
|
+
expect(@ts.status).to eq('running')
|
219
|
+
.or eq('queued')
|
220
|
+
.or eq('ready') # It takes time to load sometimes
|
221
221
|
end
|
222
222
|
|
223
223
|
it 'Raises an error if task does not exists' do
|
@@ -518,20 +518,48 @@ RSpec.describe Win32::TaskScheduler, :windows_only do
|
|
518
518
|
|
519
519
|
describe '#account_information' do
|
520
520
|
before { create_task }
|
521
|
-
|
522
|
-
|
523
|
-
password
|
524
|
-
|
525
|
-
|
521
|
+
context 'Service Account Users' do
|
522
|
+
let(:service_user) { 'LOCAL SERVICE' }
|
523
|
+
let(:password) { nil }
|
524
|
+
it 'Does not require any password' do
|
525
|
+
expect(@ts.set_account_information(service_user, password)). to be_truthy
|
526
|
+
expect(@ts.account_information.upcase).to eq(service_user)
|
527
|
+
end
|
528
|
+
it 'passing account_name will display account_simple_name' do
|
529
|
+
user = 'NT AUTHORITY\LOCAL SERVICE'
|
530
|
+
expect(@ts.set_account_information(user, password)). to be_truthy
|
531
|
+
expect(@ts.account_information.upcase).to eq(service_user)
|
532
|
+
end
|
533
|
+
it 'Raises an error if password is given' do
|
534
|
+
password = 'XYZ'
|
535
|
+
expect { @ts.set_account_information(service_user, password) }. to raise_error(tasksch_err)
|
536
|
+
end
|
526
537
|
end
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
password
|
531
|
-
|
532
|
-
|
538
|
+
context 'Built in Groups' do
|
539
|
+
let(:group_user) { 'USERS' }
|
540
|
+
let(:password) { nil }
|
541
|
+
it 'Does not require any password' do
|
542
|
+
expect(@ts.set_account_information(group_user, password)). to be_truthy
|
543
|
+
expect(@ts.account_information.upcase).to eq(group_user)
|
544
|
+
end
|
545
|
+
it 'passing account_name will display account_simple_name' do
|
546
|
+
user = 'BUILTIN\USERS'
|
547
|
+
expect(@ts.set_account_information(user, password)). to be_truthy
|
548
|
+
expect(@ts.account_information.upcase).to eq(group_user)
|
549
|
+
end
|
550
|
+
it 'Raises an error if password is given' do
|
551
|
+
password = 'XYZ'
|
552
|
+
expect { @ts.set_account_information(group_user, password) }. to raise_error(tasksch_err)
|
553
|
+
end
|
554
|
+
end
|
555
|
+
context 'Non-system users' do
|
556
|
+
it 'Require a password' do
|
557
|
+
user = ENV['user']
|
558
|
+
password = nil
|
559
|
+
expect { @ts.set_account_information(user, password) }. to raise_error(tasksch_err)
|
560
|
+
expect(@ts.account_information).to include(user)
|
561
|
+
end
|
533
562
|
end
|
534
|
-
|
535
563
|
it 'Raises an error if task does not exists' do
|
536
564
|
@ts.instance_variable_set(:@task, nil)
|
537
565
|
user = 'User'
|
@@ -640,31 +668,57 @@ RSpec.describe Win32::TaskScheduler, :windows_only do
|
|
640
668
|
before { create_task }
|
641
669
|
before { stub_user }
|
642
670
|
|
671
|
+
it 'Require a hash' do
|
672
|
+
expect { @ts.configure_settings('XYZ') }. to raise_error(TypeError)
|
673
|
+
expect(@ts.configure_settings({})).to eq({})
|
674
|
+
end
|
675
|
+
|
676
|
+
it 'Raises an error if invalid setting is passed' do
|
677
|
+
expect { @ts.configure_settings(invalid: true) }. to raise_error(TypeError)
|
678
|
+
end
|
679
|
+
|
680
|
+
it 'Sets settings of the task and returns a hash' do
|
681
|
+
settings = { allow_demand_start: false,
|
682
|
+
disallow_start_if_on_batteries: false,
|
683
|
+
enabled: false,
|
684
|
+
stop_if_going_on_batteries: false }
|
685
|
+
|
686
|
+
expect(@ts.configure_settings(settings)).to eq(settings)
|
687
|
+
expect(@ts.settings).to include(settings)
|
688
|
+
end
|
689
|
+
|
643
690
|
it 'Sets idle settings of the task' do
|
644
|
-
|
645
|
-
#
|
646
|
-
settings = { idle_duration:
|
647
|
-
wait_timeout:
|
691
|
+
# It accepts time in minutes
|
692
|
+
# and returns the result in ISO8601 format
|
693
|
+
settings = { idle_duration: 11,
|
694
|
+
wait_timeout: 10,
|
648
695
|
stop_on_idle_end: true,
|
649
696
|
restart_on_idle: false }
|
650
|
-
@ts.configure_settings(settings)
|
651
|
-
expect(@ts.idle_settings).to eq(settings)
|
652
|
-
end
|
653
697
|
|
654
|
-
|
655
|
-
|
656
|
-
|
698
|
+
result_settings = { idle_duration: 'PT11M',
|
699
|
+
wait_timeout: 'PT10M',
|
700
|
+
stop_on_idle_end: true,
|
701
|
+
restart_on_idle: false }
|
702
|
+
|
657
703
|
@ts.configure_settings(settings)
|
658
|
-
expect(@ts.
|
704
|
+
expect(@ts.idle_settings).to eq(result_settings)
|
659
705
|
end
|
660
706
|
|
661
|
-
it '
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
707
|
+
it 'Set settings and idle settings of the task' do
|
708
|
+
settings = {
|
709
|
+
allow_demand_start: false,
|
710
|
+
disallow_start_if_on_batteries: false,
|
711
|
+
stop_if_going_on_batteries: false
|
712
|
+
}
|
713
|
+
|
714
|
+
idle_settings = {
|
715
|
+
stop_on_idle_end: true,
|
716
|
+
restart_on_idle: false
|
717
|
+
}
|
718
|
+
|
719
|
+
@ts.configure_settings(settings.merge(idle_settings))
|
720
|
+
expect(@ts.settings).to include(settings)
|
721
|
+
expect(@ts.idle_settings).to include(idle_settings)
|
668
722
|
end
|
669
723
|
|
670
724
|
it 'Raises an error if task does not exists' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-taskscheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Park Heesob
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-10-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -120,6 +120,7 @@ files:
|
|
120
120
|
- lib/win32/taskscheduler/version.rb
|
121
121
|
- lib/win32/windows/constants.rb
|
122
122
|
- lib/win32/windows/helper.rb
|
123
|
+
- lib/win32/windows/sid.rb
|
123
124
|
- lib/win32/windows/time_calc_helper.rb
|
124
125
|
- spec/functional/win32/taskschedular_spec.rb
|
125
126
|
- spec/functional/win32/windows/time_calc_helper_spec.rb
|