metasploit_data_models 0.7.0-java → 0.11.2-java
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.
- data/.gitignore +3 -0
- data/.travis.yml +1 -0
- data/app/models/mdm/host.rb +352 -26
- data/app/models/mdm/loot.rb +72 -7
- data/app/models/mdm/{module_action.rb → module/action.rb} +3 -3
- data/app/models/mdm/{module_arch.rb → module/arch.rb} +3 -3
- data/app/models/mdm/{module_author.rb → module/author.rb} +3 -3
- data/app/models/mdm/module/detail.rb +280 -0
- data/app/models/mdm/{module_mixin.rb → module/mixin.rb} +3 -3
- data/app/models/mdm/{module_platform.rb → module/platform.rb} +3 -3
- data/app/models/mdm/module/ref.rb +48 -0
- data/app/models/mdm/{module_target.rb → module/target.rb} +3 -3
- data/app/models/mdm/note.rb +61 -6
- data/app/models/mdm/ref.rb +39 -1
- data/app/models/mdm/service.rb +85 -7
- data/app/models/mdm/session.rb +100 -6
- data/app/models/mdm/vuln.rb +104 -24
- data/db/migrate/20130228214900_change_required_columns_to_null_false_in_web_vulns.rb +1 -17
- data/db/migrate/20130412154159_change_foreign_key_in_module_actions.rb +25 -0
- data/db/migrate/20130412171844_change_foreign_key_in_module_archs.rb +25 -0
- data/db/migrate/20130412173121_change_foreign_key_in_module_authors.rb +25 -0
- data/db/migrate/20130412173640_change_foreign_key_in_module_mixins.rb +25 -0
- data/db/migrate/20130412174254_change_foreign_key_in_module_platforms.rb +25 -0
- data/db/migrate/20130412174719_change_foreign_key_in_module_refs.rb +25 -0
- data/db/migrate/20130412175040_change_foreign_key_in_module_targets.rb +25 -0
- data/db/migrate/20130430151353_change_required_columns_to_null_false_in_hosts.rb +11 -0
- data/db/migrate/20130430162145_enforce_address_uniqueness_in_workspace_in_hosts.rb +23 -0
- data/lib/mdm/module.rb +4 -0
- data/lib/metasploit_data_models.rb +1 -0
- data/lib/metasploit_data_models/change_required_columns_to_null_false.rb +23 -0
- data/lib/metasploit_data_models/version.rb +1 -1
- data/spec/app/models/mdm/host_spec.rb +411 -0
- data/spec/app/models/mdm/host_tag_spec.rb +13 -0
- data/spec/app/models/mdm/{module_action_spec.rb → module/action_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_arch_spec.rb → module/arch_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_author_spec.rb → module/author_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_detail_spec.rb → module/detail_spec.rb} +101 -11
- data/spec/app/models/mdm/{module_mixin_spec.rb → module/mixin_spec.rb} +6 -6
- data/spec/app/models/mdm/{module_platform_spec.rb → module/platform_spec.rb} +6 -6
- data/spec/app/models/mdm/module/ref_spec.rb +62 -0
- data/spec/app/models/mdm/{module_target_spec.rb → module/target_spec.rb} +6 -6
- data/spec/app/models/mdm/ref_spec.rb +62 -0
- data/spec/app/models/mdm/tag_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_ref_spec.rb +13 -0
- data/spec/app/models/mdm/vuln_spec.rb +231 -0
- data/spec/dummy/db/schema.rb +20 -20
- data/spec/factories/mdm/host_tags.rb +9 -0
- data/spec/factories/mdm/hosts.rb +65 -0
- data/spec/factories/mdm/module/actions.rb +14 -0
- data/spec/factories/mdm/module/archs.rb +14 -0
- data/spec/factories/mdm/{module_authors.rb → module/authors.rb} +4 -4
- data/spec/factories/mdm/module/details.rb +66 -0
- data/spec/factories/mdm/module/mixins.rb +14 -0
- data/spec/factories/mdm/module/platforms.rb +14 -0
- data/spec/factories/mdm/module/refs.rb +14 -0
- data/spec/factories/mdm/{module_targets.rb → module/targets.rb} +3 -3
- data/spec/factories/mdm/refs.rb +9 -0
- data/spec/factories/mdm/tags.rb +14 -0
- data/spec/factories/mdm/vuln_refs.rb +4 -0
- data/spec/factories/mdm/vulns.rb +20 -0
- metadata +75 -42
- data/app/models/mdm/module_detail.rb +0 -59
- data/app/models/mdm/module_ref.rb +0 -24
- data/spec/app/models/mdm/module_ref_spec.rb +0 -38
- data/spec/factories/mdm/module_actions.rb +0 -14
- data/spec/factories/mdm/module_archs.rb +0 -14
- data/spec/factories/mdm/module_details.rb +0 -9
- data/spec/factories/mdm/module_mixins.rb +0 -14
- data/spec/factories/mdm/module_platforms.rb +0 -14
- data/spec/factories/mdm/module_refs.rb +0 -14
data/app/models/mdm/loot.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
+
# Loot gathered from {#host} or {#service} such as files to prove you were on the system or to crack later to gain
|
2
|
+
# sessions on other machines in the network.
|
1
3
|
class Mdm::Loot < ActiveRecord::Base
|
2
|
-
#
|
3
|
-
# Callbacks
|
4
|
-
#
|
5
|
-
|
6
|
-
before_destroy :delete_file
|
7
|
-
|
8
4
|
#
|
9
5
|
# CONSTANTS
|
10
6
|
#
|
@@ -17,13 +13,78 @@ class Mdm::Loot < ActiveRecord::Base
|
|
17
13
|
]
|
18
14
|
|
19
15
|
#
|
20
|
-
#
|
16
|
+
# Associations
|
21
17
|
#
|
22
18
|
|
19
|
+
# @!attribute [rw] host
|
20
|
+
# The host from which the loot was gathered.
|
21
|
+
#
|
22
|
+
# @return [Mdm::Host]
|
23
23
|
belongs_to :host, :class_name => 'Mdm::Host'
|
24
|
+
|
25
|
+
# @!attribute [rw] service
|
26
|
+
# The service running on the {#host} from which the loot was gathered.
|
27
|
+
#
|
28
|
+
# @return [Mdm::Service]
|
24
29
|
belongs_to :service, :class_name => 'Mdm::Service'
|
30
|
+
|
31
|
+
# @!attribute [rw] workspace
|
32
|
+
# The workspace in which the loot is stored and the {#host} exists.
|
33
|
+
#
|
34
|
+
# @return [Mdm::Workspace]
|
25
35
|
belongs_to :workspace, :class_name => 'Mdm::Workspace'
|
26
36
|
|
37
|
+
#
|
38
|
+
# Attributes
|
39
|
+
#
|
40
|
+
|
41
|
+
# @!attribute [rw] content_type
|
42
|
+
# The mime/content type of the file at {#path}. Used to server the file correctly so browsers understand whether
|
43
|
+
# to render or download the file.
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
|
47
|
+
# @!attribute [rw] created_at
|
48
|
+
# When the loot was created.
|
49
|
+
#
|
50
|
+
# @return [DateTime]
|
51
|
+
|
52
|
+
# @!attribute [rw] data
|
53
|
+
# Loot data not stored in file at {#path}.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
|
57
|
+
# @!attribute [rw] ltype
|
58
|
+
# The type of loot
|
59
|
+
#
|
60
|
+
# @return [String]
|
61
|
+
|
62
|
+
# @!attribute [rw] info
|
63
|
+
# Information about the loot.
|
64
|
+
#
|
65
|
+
# @return [String]
|
66
|
+
|
67
|
+
# @!attribute [rw] name
|
68
|
+
# The name of the loot.
|
69
|
+
#
|
70
|
+
# @return [String]
|
71
|
+
|
72
|
+
# @!attribute [rw] path
|
73
|
+
# The on-disk path to the loot file.
|
74
|
+
#
|
75
|
+
# @return [String]
|
76
|
+
|
77
|
+
# @!attribute [rw] updated_at
|
78
|
+
# The last time the loot was updated.
|
79
|
+
#
|
80
|
+
# @return [DateTime]
|
81
|
+
|
82
|
+
#
|
83
|
+
# Callbacks
|
84
|
+
#
|
85
|
+
|
86
|
+
before_destroy :delete_file
|
87
|
+
|
27
88
|
#
|
28
89
|
# Scopes
|
29
90
|
#
|
@@ -49,6 +110,10 @@ class Mdm::Loot < ActiveRecord::Base
|
|
49
110
|
|
50
111
|
private
|
51
112
|
|
113
|
+
# Deletes {#path} from disk.
|
114
|
+
#
|
115
|
+
# @todo https://www.pivotaltracker.com/story/show/49023795
|
116
|
+
# @return [void]
|
52
117
|
def delete_file
|
53
118
|
c = Pro::Client.get rescue nil
|
54
119
|
if c
|
@@ -1,11 +1,11 @@
|
|
1
|
-
class Mdm::
|
1
|
+
class Mdm::Module::Action < ActiveRecord::Base
|
2
2
|
self.table_name = 'module_actions'
|
3
3
|
|
4
4
|
#
|
5
5
|
# Associations
|
6
6
|
#
|
7
7
|
|
8
|
-
belongs_to :
|
8
|
+
belongs_to :detail, :class_name => 'Mdm::Module::Detail'
|
9
9
|
|
10
10
|
#
|
11
11
|
# Mass Assignment Security
|
@@ -17,7 +17,7 @@ class Mdm::ModuleAction < ActiveRecord::Base
|
|
17
17
|
# Validations
|
18
18
|
#
|
19
19
|
|
20
|
-
validates :
|
20
|
+
validates :detail, :presence => true
|
21
21
|
validates :name, :presence => true
|
22
22
|
|
23
23
|
ActiveSupport.run_load_hooks(:mdm_module_action, self)
|
@@ -1,11 +1,11 @@
|
|
1
|
-
class Mdm::
|
1
|
+
class Mdm::Module::Arch < ActiveRecord::Base
|
2
2
|
self.table_name = 'module_archs'
|
3
3
|
|
4
4
|
#
|
5
5
|
# Associations
|
6
6
|
#
|
7
7
|
|
8
|
-
belongs_to :
|
8
|
+
belongs_to :detail, :class_name => 'Mdm::Module::Detail'
|
9
9
|
|
10
10
|
#
|
11
11
|
# Mass Assignment Security
|
@@ -17,7 +17,7 @@ class Mdm::ModuleArch < ActiveRecord::Base
|
|
17
17
|
# Validations
|
18
18
|
#
|
19
19
|
|
20
|
-
validates :
|
20
|
+
validates :detail, :presence => true
|
21
21
|
validates :name, :presence => true
|
22
22
|
|
23
23
|
ActiveSupport.run_load_hooks(:mdm_module_arch, self)
|
@@ -1,11 +1,11 @@
|
|
1
|
-
class Mdm::
|
1
|
+
class Mdm::Module::Author < ActiveRecord::Base
|
2
2
|
self.table_name = 'module_authors'
|
3
3
|
|
4
4
|
#
|
5
5
|
# Associations
|
6
6
|
#
|
7
7
|
|
8
|
-
belongs_to :
|
8
|
+
belongs_to :detail, :class_name => 'Mdm::Module::Detail'
|
9
9
|
|
10
10
|
#
|
11
11
|
# Mass Assignment Security
|
@@ -18,7 +18,7 @@ class Mdm::ModuleAuthor < ActiveRecord::Base
|
|
18
18
|
# Validations
|
19
19
|
#
|
20
20
|
|
21
|
-
validates :
|
21
|
+
validates :detail, :presence => true
|
22
22
|
validates :name, :presence => true
|
23
23
|
|
24
24
|
ActiveSupport.run_load_hooks(:mdm_module_author, self)
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# Details about an Msf::Module. Metadata that can be an array is stored in associations in modules under the
|
2
|
+
# {Mdm::Module} namespace.
|
3
|
+
class Mdm::Module::Detail < ActiveRecord::Base
|
4
|
+
self.table_name = 'module_details'
|
5
|
+
|
6
|
+
#
|
7
|
+
# CONSTANTS
|
8
|
+
#
|
9
|
+
|
10
|
+
# The directory for a given {#mtype} is a not always the pluralization of {#mtype}, so this maps the {#mtype} to the
|
11
|
+
# type directory that is used to generate the {#file} from the {#mtype} and {#refname}.
|
12
|
+
DIRECTORY_BY_TYPE = {
|
13
|
+
'auxiliary' => 'auxiliary',
|
14
|
+
'encoder' => 'encoders',
|
15
|
+
'exploit' => 'exploits',
|
16
|
+
'nop' => 'nops',
|
17
|
+
'payload' => 'payloads',
|
18
|
+
'post' => 'post'
|
19
|
+
}
|
20
|
+
|
21
|
+
# {#privileged} is Boolean so, valid values are just `true` and `false`, but since both the validation and
|
22
|
+
# factory need an array of valid values, this constant exists.
|
23
|
+
PRIVILEGES = [
|
24
|
+
false,
|
25
|
+
true
|
26
|
+
]
|
27
|
+
|
28
|
+
# Converts {#rank}, which is an Integer, to the name used for that rank.
|
29
|
+
RANK_BY_NAME = {
|
30
|
+
'Manual' => 0,
|
31
|
+
'Low' => 100,
|
32
|
+
'Average' => 200,
|
33
|
+
'Normal' => 300,
|
34
|
+
'Good' => 400,
|
35
|
+
'Great' => 500,
|
36
|
+
'Excellent' => 600
|
37
|
+
}
|
38
|
+
|
39
|
+
# Valid values for {#stance}.
|
40
|
+
STANCES = [
|
41
|
+
'aggressive',
|
42
|
+
'passive'
|
43
|
+
]
|
44
|
+
|
45
|
+
#
|
46
|
+
# Associations
|
47
|
+
#
|
48
|
+
|
49
|
+
# @!attribute [rw] actions
|
50
|
+
# Auxiliary actions to perform when this running this module.
|
51
|
+
#
|
52
|
+
# @return [Array<Mdm::Module::Action>]
|
53
|
+
has_many :actions, :class_name => 'Mdm::Module::Action', :dependent => :destroy
|
54
|
+
|
55
|
+
# @!attribute [rw] archs
|
56
|
+
# Architectures supported by this module.
|
57
|
+
#
|
58
|
+
# @return [Array<Mdm::Module::Arch>]
|
59
|
+
has_many :archs, :class_name => 'Mdm::Module::Arch', :dependent => :destroy
|
60
|
+
|
61
|
+
# @!attribute [rw] authors
|
62
|
+
# Authors (and their emails) of this module. Usually includes the original discoverer who wrote the
|
63
|
+
# proof-of-concept and then the people that ported the proof-of-concept to metasploit-framework.
|
64
|
+
#
|
65
|
+
# @return [Array<Mdm::Module::Mixin>]
|
66
|
+
has_many :authors, :class_name => 'Mdm::Module::Author', :dependent => :destroy
|
67
|
+
|
68
|
+
# @!attribute [rw] mixins
|
69
|
+
# Mixins used by this module.
|
70
|
+
#
|
71
|
+
# @return [Array<Mdm::Module::Mixin>]
|
72
|
+
has_many :mixins, :class_name => 'Mdm::Module::Mixin', :dependent => :destroy
|
73
|
+
|
74
|
+
# @!attribute [rw] platforms
|
75
|
+
# Platforms supported by this module.
|
76
|
+
#
|
77
|
+
# @return [Array<Mdm::Module::Platform>]
|
78
|
+
has_many :platforms, :class_name => 'Mdm::Module::Platform', :dependent => :destroy
|
79
|
+
|
80
|
+
# @!attribute [rw] refs
|
81
|
+
# External references to the vulnerabilities this module exploits.
|
82
|
+
#
|
83
|
+
# @return [Array<Mdm::Module::Ref>]
|
84
|
+
has_many :refs, :class_name => 'Mdm::Module::Ref', :dependent => :destroy
|
85
|
+
|
86
|
+
# @!attribute [rw] targets
|
87
|
+
# Names of targets with different configurations that can be exploited by this module.
|
88
|
+
#
|
89
|
+
# @return [Array<Mdm::Module::Target>]
|
90
|
+
has_many :targets, :class_name => 'Mdm::Module::Target', :dependent => :destroy
|
91
|
+
|
92
|
+
#
|
93
|
+
# Attributes
|
94
|
+
#
|
95
|
+
|
96
|
+
# @!attribute [rw] default_action
|
97
|
+
# Name of the default action in {#actions}.
|
98
|
+
#
|
99
|
+
# @return [String] {Mdm::Module::Action#name}.
|
100
|
+
|
101
|
+
# @!attribute [rw] default_target
|
102
|
+
# Name of the default target in {#targets}.
|
103
|
+
#
|
104
|
+
# @return [String] {Mdm::Module::Target#name}.
|
105
|
+
|
106
|
+
# @!attribute [rw] description
|
107
|
+
# A long, paragraph description of what the module does.
|
108
|
+
#
|
109
|
+
# @return [String]
|
110
|
+
|
111
|
+
# @!attribute [rw] disclosure_date
|
112
|
+
# The date the vulnerability exploited by this module was disclosed to the public.
|
113
|
+
#
|
114
|
+
# @return [DateTime]
|
115
|
+
|
116
|
+
# @!attribute [rw] file
|
117
|
+
# The full path to the module file on-disk.
|
118
|
+
#
|
119
|
+
# @return [String]
|
120
|
+
|
121
|
+
# @!attribute [rw] fullname
|
122
|
+
# The full name of the module. The full name is "{#mtype}/{#refname}".
|
123
|
+
#
|
124
|
+
# @return [String]
|
125
|
+
|
126
|
+
# @!attribute [rw] license
|
127
|
+
# The name of the software license for the module's code.
|
128
|
+
#
|
129
|
+
# @return [String]
|
130
|
+
|
131
|
+
# @!attribute [rw] mtime
|
132
|
+
# The modification time of the module file on-disk.
|
133
|
+
#
|
134
|
+
# @return [DateTime]
|
135
|
+
|
136
|
+
# @!attribute [rw] mtype
|
137
|
+
# The type of the module.
|
138
|
+
#
|
139
|
+
# @return [String] key in {DIRECTORY_BY_TYPE}
|
140
|
+
|
141
|
+
# @!attribute [rw] name
|
142
|
+
# The human readable name of the module. It is unrelated to {#fullname} or {#refname} and is better thought of
|
143
|
+
# as a short summary of the {#description}.
|
144
|
+
#
|
145
|
+
# @return [String]
|
146
|
+
|
147
|
+
# @!attribute [rw] privileged
|
148
|
+
# Whether this module requires priveleged access to run.
|
149
|
+
#
|
150
|
+
# @return [Boolean]
|
151
|
+
|
152
|
+
# @!attribute [rw] rank
|
153
|
+
# The reliability of the module and likelyhood that the module won't knock over the service or host being exploited.
|
154
|
+
# Bigger values is better.
|
155
|
+
#
|
156
|
+
# @return [Integer]
|
157
|
+
|
158
|
+
# @!attribute [rw] ready
|
159
|
+
# Boolean indicating whether the metadata for the module has been updated from the on-disk module.
|
160
|
+
#
|
161
|
+
# @return [false] if the associations are still being updated.
|
162
|
+
# @return [true] if this detail and its associations are up-to-date.
|
163
|
+
|
164
|
+
# @!attribute [rw] refname
|
165
|
+
# The reference name of the module.
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
|
169
|
+
# @!attribute [rw] stance
|
170
|
+
# Whether the module is active or passive.
|
171
|
+
#
|
172
|
+
# @return ['active', 'passive']
|
173
|
+
|
174
|
+
#
|
175
|
+
# Validations
|
176
|
+
#
|
177
|
+
|
178
|
+
validates :mtype,
|
179
|
+
:inclusion => {
|
180
|
+
:in => DIRECTORY_BY_TYPE.keys
|
181
|
+
}
|
182
|
+
validates :privileged,
|
183
|
+
:inclusion => {
|
184
|
+
:in => PRIVILEGES
|
185
|
+
}
|
186
|
+
validates :rank,
|
187
|
+
:inclusion => {
|
188
|
+
:in => RANK_BY_NAME.values
|
189
|
+
},
|
190
|
+
:numericality => {
|
191
|
+
:only_integer => true
|
192
|
+
}
|
193
|
+
validates :refname, :presence => true
|
194
|
+
validates :stance,
|
195
|
+
:inclusion => {
|
196
|
+
:in => STANCES
|
197
|
+
}
|
198
|
+
|
199
|
+
validates_associated :actions
|
200
|
+
validates_associated :archs
|
201
|
+
validates_associated :authors
|
202
|
+
validates_associated :mixins
|
203
|
+
validates_associated :platforms
|
204
|
+
validates_associated :refs
|
205
|
+
validates_associated :targets
|
206
|
+
|
207
|
+
# Adds an {Mdm::Module::Action} with the given {Mdm::Module::Action#name} to {#actions} and immediately saves it to
|
208
|
+
# the database.
|
209
|
+
#
|
210
|
+
# @param name [String] {Mdm::Module::Action#name}.
|
211
|
+
# @return [true] if save was successful.
|
212
|
+
# @return [false] if save was unsucessful.
|
213
|
+
def add_action(name)
|
214
|
+
self.actions.build(:name => name).save
|
215
|
+
end
|
216
|
+
|
217
|
+
# Adds an {Mdm::Module::Arch} with the given {Mdm::Module::Arch#name} to {#archs} and immediately saves it to the
|
218
|
+
# database.
|
219
|
+
#
|
220
|
+
# @param name [String] {Mdm::Module::Arch#name}.
|
221
|
+
# @return [true] if save was successful.
|
222
|
+
# @return [false] if save was unsuccessful.
|
223
|
+
def add_arch(name)
|
224
|
+
self.archs.build(:name => name).save
|
225
|
+
end
|
226
|
+
|
227
|
+
# Adds an {Mdm::Module::Author} with the given {Mdm::Module::Author#name} and {Mdm::Module::Author#email} to
|
228
|
+
# {#authors} and immediately saves it to the database.
|
229
|
+
#
|
230
|
+
# @param name [String] {Mdm::Module::Author#name}.
|
231
|
+
# @param email [String] {Mdm::Module::Author#email}.
|
232
|
+
# @return [true] if save was successful.
|
233
|
+
# @return [false] if save was unsuccessful.
|
234
|
+
def add_author(name, email=nil)
|
235
|
+
self.authors.build(:name => name, :email => email).save
|
236
|
+
end
|
237
|
+
|
238
|
+
# Adds an {Mdm::Module::Mixin} with the given {Mdm::Module::Mixin#name} to {#mixins} and immediately saves it to the
|
239
|
+
# database.
|
240
|
+
#
|
241
|
+
# @param name [String] {Mdm::Module::Mixin#name}.
|
242
|
+
# @return [true] if save was successful.
|
243
|
+
# @return [false] if save was unsuccessful.
|
244
|
+
def add_mixin(name)
|
245
|
+
self.mixins.build(:name => name).save
|
246
|
+
end
|
247
|
+
|
248
|
+
# Adds an {Mdm::Module::Platform} with the given {Mdm::Module::Platform#name} to {#platforms} and immediately saves it
|
249
|
+
# to the database.
|
250
|
+
#
|
251
|
+
# @param name [String] {Mdm::Module::Platform#name}.
|
252
|
+
# @return [true] if save was successful.
|
253
|
+
# @return [false] if save was unsuccessful.
|
254
|
+
def add_platform(name)
|
255
|
+
self.platforms.build(:name => name).save
|
256
|
+
end
|
257
|
+
|
258
|
+
# Adds an {Mdm::Module::Ref} with the given {Mdm::Module::Ref#name} to {#refs} and immediately saves it to the
|
259
|
+
# database.
|
260
|
+
#
|
261
|
+
# @param name [String] {Mdm::Module::Ref#name}.
|
262
|
+
# @return [true] if save was successful.
|
263
|
+
# @return [false] if save was unsuccessful.
|
264
|
+
def add_ref(name)
|
265
|
+
self.refs.build(:name => name).save
|
266
|
+
end
|
267
|
+
|
268
|
+
# Adds an {Mdm::Module::Target} with the given {Mdm::Module::Target#index} and {Mdm::Module::Target#name} to
|
269
|
+
# {#targets} and immediately saves it to the database.
|
270
|
+
#
|
271
|
+
# @param index [Integer] index of target among other {#targets}.
|
272
|
+
# @param name [String] {Mdm::Module::Target#name}.
|
273
|
+
# @return [true] if save was successful.
|
274
|
+
# @return [false] if save was unsuccessful.
|
275
|
+
def add_target(index, name)
|
276
|
+
self.targets.build(:index => index, :name => name).save
|
277
|
+
end
|
278
|
+
|
279
|
+
ActiveSupport.run_load_hooks(:mdm_module_detail, self)
|
280
|
+
end
|