gamekey 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/codeStyleSettings.xml +13 -0
- data/.idea/encodings.xml +6 -0
- data/.idea/gamekey.iml +97 -0
- data/.idea/misc.xml +14 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +946 -0
- data/Dockerfile +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +133 -0
- data/Rakefile +1 -0
- data/bin/gamekey +232 -0
- data/gamekey.gemspec +32 -0
- data/lib/auth.rb +14 -0
- data/lib/defaults.rb +47 -0
- data/lib/gamekey.rb +632 -0
- data/lib/gamekey/version.rb +3 -0
- data/lib/reference.rb +854 -0
- data/projectFilesBackup/.idea/gamekey.iml +8 -0
- metadata +167 -0
data/lib/defaults.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "securerandom"
|
2
|
+
require "digest"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Defines all CONSTANTS for Gamekey.
|
7
|
+
#
|
8
|
+
module Defaults
|
9
|
+
|
10
|
+
# Default port number
|
11
|
+
PORT = 8080
|
12
|
+
|
13
|
+
# Default storage file
|
14
|
+
STORAGE = "gamekey.json"
|
15
|
+
|
16
|
+
# Initial storage content
|
17
|
+
DB = {
|
18
|
+
service: "Gamekey",
|
19
|
+
storage: SecureRandom.uuid,
|
20
|
+
version: "0.0.2",
|
21
|
+
users: [],
|
22
|
+
games: [],
|
23
|
+
gamestates: []
|
24
|
+
}
|
25
|
+
|
26
|
+
# Used crpyto hash function
|
27
|
+
CRYPTOHASH = Digest::SHA256.new
|
28
|
+
|
29
|
+
# Default test host of gamekey service
|
30
|
+
TESTHOST = "http://localhost:#{PORT}"
|
31
|
+
|
32
|
+
# Regular Expression to validate Email adresses
|
33
|
+
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
34
|
+
|
35
|
+
# Regular Expression to validate http and https uris adresses
|
36
|
+
VALID_URL_REGEX = /\A#{URI::regexp(['http', 'https'])}\z/
|
37
|
+
|
38
|
+
# A Test hash for testing gamestate handling
|
39
|
+
TESTHASH = {
|
40
|
+
'this' => 'is',
|
41
|
+
'a' => ['simple', 'test']
|
42
|
+
}
|
43
|
+
|
44
|
+
# A Test list for testing gamestate handling
|
45
|
+
TESTLIST = ['This', 'is', 'a', 'Test']
|
46
|
+
|
47
|
+
end
|
data/lib/gamekey.rb
ADDED
@@ -0,0 +1,632 @@
|
|
1
|
+
require "gamekey/version"
|
2
|
+
require "sinatra/base"
|
3
|
+
require 'sinatra/cross_origin'
|
4
|
+
|
5
|
+
require "defaults"
|
6
|
+
require "auth"
|
7
|
+
require "json"
|
8
|
+
require "securerandom"
|
9
|
+
require "digest"
|
10
|
+
|
11
|
+
require 'colorize'
|
12
|
+
|
13
|
+
#
|
14
|
+
# This class defines the REST API for the gamekey service.
|
15
|
+
#
|
16
|
+
class GamekeyService
|
17
|
+
|
18
|
+
# Path to storage file
|
19
|
+
attr_reader :storage
|
20
|
+
|
21
|
+
# Port
|
22
|
+
attr_reader :port
|
23
|
+
|
24
|
+
# Hash of the in memory database
|
25
|
+
attr_reader :memory
|
26
|
+
|
27
|
+
# Constructor to create a Gamekey service
|
28
|
+
#
|
29
|
+
def initialize(storage: Defaults::STORAGE, port: Defaults::PORT)
|
30
|
+
@storage = storage
|
31
|
+
@port = port
|
32
|
+
|
33
|
+
File.open(storage, "w") { |file| file.write(Defaults::DB.to_json) } unless File.exist?(storage)
|
34
|
+
content = File.read(storage)
|
35
|
+
|
36
|
+
# print(content)
|
37
|
+
@memory = JSON.parse(content)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Gets user hash by id from memory.
|
41
|
+
# return nil, if id is not present
|
42
|
+
#
|
43
|
+
def get_user_by_id(id)
|
44
|
+
@memory['users'].select { |user| user['id'] == id }.first
|
45
|
+
end
|
46
|
+
|
47
|
+
# Gets user hash by name from memory.
|
48
|
+
# return nil, if name is not present
|
49
|
+
#
|
50
|
+
def get_user_by_name(name)
|
51
|
+
@memory['users'].select { |user| user['name'] == name }.first
|
52
|
+
end
|
53
|
+
|
54
|
+
# Gets game hash by id from memory.
|
55
|
+
# return nil, if id is not present
|
56
|
+
#
|
57
|
+
def get_game_by_id(id)
|
58
|
+
@memory['games'].select { |game| game['id'] == id }.first
|
59
|
+
end
|
60
|
+
|
61
|
+
# Defines the CORS enabled REST API of the Gamekey service.
|
62
|
+
# Following resources are managed:
|
63
|
+
#
|
64
|
+
# - User
|
65
|
+
# - Game
|
66
|
+
# - Gamestate
|
67
|
+
#
|
68
|
+
def api()
|
69
|
+
|
70
|
+
memory = @memory
|
71
|
+
storage = @storage
|
72
|
+
port = @port
|
73
|
+
service = self
|
74
|
+
|
75
|
+
Sinatra.new do
|
76
|
+
|
77
|
+
register Sinatra::CrossOrigin
|
78
|
+
|
79
|
+
set :bind, "0.0.0.0"
|
80
|
+
set :port, port
|
81
|
+
enable :cross_origin
|
82
|
+
set :allow_origin, :any
|
83
|
+
set :allow_methods, [:get, :post, :options, :delete, :put]
|
84
|
+
|
85
|
+
#
|
86
|
+
# Standard 404 message
|
87
|
+
#
|
88
|
+
not_found do
|
89
|
+
"not found"
|
90
|
+
end
|
91
|
+
|
92
|
+
options "*" do
|
93
|
+
response.headers["Allow"] = "HEAD,GET,PUT,POST,DELETE,OPTIONS"
|
94
|
+
response.headers["Access-Control-Allow-Headers"] = "charset, pwd, secret, name, mail, newpwd"
|
95
|
+
200
|
96
|
+
end
|
97
|
+
|
98
|
+
#
|
99
|
+
# API Endpoints for User resources
|
100
|
+
#
|
101
|
+
|
102
|
+
#
|
103
|
+
# Lists all registered users.
|
104
|
+
#
|
105
|
+
# @return 200 OK, Response body includes JSON list of all registered users, list might be empty)
|
106
|
+
# Due to the fact that this request can be send unauthenticated, user data never!!! include data
|
107
|
+
# about games that are played by a user.
|
108
|
+
#
|
109
|
+
get "/users" do
|
110
|
+
JSON.pretty_generate(memory['users'])
|
111
|
+
end
|
112
|
+
|
113
|
+
#
|
114
|
+
# Creates a user.
|
115
|
+
#
|
116
|
+
# @param pwd Password for the user (used for authentication). Required. Parameter is part of request body.
|
117
|
+
# @param name Name of the user to provided new name. Required. Parameter is part of request body.
|
118
|
+
# @param mail Mail of the user. Optional. Parameter is part of request body.
|
119
|
+
#
|
120
|
+
# @return 200 OK, on successfull creation (response body includes JSON representation of updated user)
|
121
|
+
# @return 400, on invalid mail (response body includes error message)
|
122
|
+
# @return 409, on already existing new name (response body includes error message)
|
123
|
+
#
|
124
|
+
post "/user" do
|
125
|
+
|
126
|
+
name = params['name']
|
127
|
+
pwd = params['pwd']
|
128
|
+
mail = params['mail']
|
129
|
+
id = SecureRandom.uuid
|
130
|
+
|
131
|
+
unless mail =~ Defaults::VALID_EMAIL_REGEX || mail == nil
|
132
|
+
status 400
|
133
|
+
return "Bad Request: '#{mail}' is not a valid email."
|
134
|
+
end
|
135
|
+
|
136
|
+
if memory['users'].map { |entity| entity['name'] }.include? name
|
137
|
+
status 409
|
138
|
+
return "User with name '#{params['name']}' exists already."
|
139
|
+
end
|
140
|
+
|
141
|
+
user = {
|
142
|
+
"type" => 'user',
|
143
|
+
"name" => params['name'],
|
144
|
+
"id" => id,
|
145
|
+
"created" => "#{ Time.now.utc.iso8601(6) }",
|
146
|
+
"mail" => mail,
|
147
|
+
"signature" => Auth::signature(id, pwd)
|
148
|
+
}
|
149
|
+
|
150
|
+
memory['users'] << user
|
151
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
152
|
+
JSON.pretty_generate(user)
|
153
|
+
end
|
154
|
+
|
155
|
+
#
|
156
|
+
# Retrieves user data.
|
157
|
+
#
|
158
|
+
# @param :id Unique identifier (or name, if called by byname option )of the user (the id is never changed!). Required. Parameter is part of the REST-URI!
|
159
|
+
# @param pwd Existing password of the user (used for authentication). Required. Parameter is part of request body.
|
160
|
+
# @param byname {=true, =false} Indicates that look up should be done by name (and not by identifier, which is the default). Optional. Parameter is part of request body.
|
161
|
+
|
162
|
+
# @return 200 OK, response body includes JSON representation of user
|
163
|
+
# @return 400, Bad Request, if byname parameter is set but not set to 'true' or 'false'
|
164
|
+
# @return 401, if request is not provided with correct password (response body includes error message)
|
165
|
+
# @return 404, if user with id is not present (response body includes error message)
|
166
|
+
#
|
167
|
+
get "/user/:id" do
|
168
|
+
pwd = params['pwd']
|
169
|
+
id = params['id']
|
170
|
+
byname = params['byname']
|
171
|
+
|
172
|
+
if !byname.nil? && byname != 'true' && byname != 'false'
|
173
|
+
status 400
|
174
|
+
return "Bad Request: byname parameter must be 'true' or 'false' (if set), was '#{byname}'."
|
175
|
+
end
|
176
|
+
|
177
|
+
user = service.get_user_by_id(id) if byname == 'false' || byname == nil
|
178
|
+
user = service.get_user_by_name(URI.decode(id)) if byname == 'true'
|
179
|
+
|
180
|
+
if user == nil
|
181
|
+
status 404
|
182
|
+
return "not found"
|
183
|
+
end
|
184
|
+
|
185
|
+
user = user.clone
|
186
|
+
|
187
|
+
unless Auth::authentic?(user, pwd)
|
188
|
+
status 401
|
189
|
+
return "unauthorized, please provide correct credentials"
|
190
|
+
end
|
191
|
+
|
192
|
+
user['games'] = memory['gamestates'].select { |state| state['userid'] == user['id'] }
|
193
|
+
.map { |state| state['gameid'] }
|
194
|
+
.uniq
|
195
|
+
|
196
|
+
JSON.pretty_generate(user)
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
# Updates a user.
|
201
|
+
#
|
202
|
+
# @param :id Unique identifier of the user (the id is never changed!). Required. Parameter is part of the REST-URI!
|
203
|
+
# @param pwd Existing password of the user (used for authentication). Required. Parameter is part of request body.
|
204
|
+
# @param new_name Changes name of the user to provided new name. Optional. Parameter is part of request body.
|
205
|
+
# @param new_mail Changes mail of the user to provided new mail. Optional. Parameter is part of request body.
|
206
|
+
# @param new_pwd Changes password of the user to a new password. Optional. Parameter is part of request body.
|
207
|
+
#
|
208
|
+
# @return 200 OK, on successfull update (response body includes JSON representation of updated user)
|
209
|
+
# @return 400, on invalid mail (response body includes error message)
|
210
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
211
|
+
# @return 409, on already existing new name (response body includes error message)
|
212
|
+
#
|
213
|
+
put "/user/:id" do
|
214
|
+
id = params['id']
|
215
|
+
pwd = params['pwd']
|
216
|
+
new_name = params['name']
|
217
|
+
new_mail = params['mail']
|
218
|
+
new_pwd = params['newpwd']
|
219
|
+
|
220
|
+
if new_mail
|
221
|
+
unless new_mail =~ Defaults::VALID_EMAIL_REGEX
|
222
|
+
status 400
|
223
|
+
return "Bad Request: '#{new_mail}' is not a valid email."
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
if memory['users'].map { |entity| entity['name'] }.include? new_name
|
228
|
+
status 409
|
229
|
+
return "User with name '#{new_name}' exists already."
|
230
|
+
end
|
231
|
+
|
232
|
+
begin
|
233
|
+
user = service.get_user_by_id(id)
|
234
|
+
|
235
|
+
unless Auth::authentic?(user, pwd)
|
236
|
+
status 401
|
237
|
+
return "unauthorized, please provide correct credentials"
|
238
|
+
end
|
239
|
+
|
240
|
+
user['name'] = new_name if new_name != nil
|
241
|
+
user['mail'] = new_mail if new_mail != nil
|
242
|
+
user['signature'] = Auth::signature(id, new_pwd) if new_pwd != nil
|
243
|
+
user['update'] = "#{ Time.now.utc.iso8601(6) }"
|
244
|
+
|
245
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
246
|
+
return JSON.pretty_generate(user)
|
247
|
+
rescue Exception => ex
|
248
|
+
status 401
|
249
|
+
return "#{ex}\nunauthorized, please provide correct credentials"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# Deletes a user and all of its associated game states.
|
255
|
+
#
|
256
|
+
# @param :id Unique identifier of the user (used for authentication). Required. Parameter is part of the REST-URI!
|
257
|
+
# @param pwd Existing password of the user (used for authentication). Required. Parameter is part of request body.
|
258
|
+
#
|
259
|
+
# @return 200 OK, on successfull delete (response body includes confirmation message)
|
260
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
261
|
+
#
|
262
|
+
delete "/user/:id" do
|
263
|
+
|
264
|
+
id = params['id']
|
265
|
+
pwd = params['pwd']
|
266
|
+
|
267
|
+
user = service.get_user_by_id(id)
|
268
|
+
|
269
|
+
if user == nil
|
270
|
+
# This is an idempotent operation.
|
271
|
+
return "User '#{id}' deleted successfully."
|
272
|
+
end
|
273
|
+
|
274
|
+
unless Auth::authentic?(user, pwd)
|
275
|
+
status 401
|
276
|
+
return "unauthorized, please provide correct credentials"
|
277
|
+
end
|
278
|
+
|
279
|
+
memory['users'].delete_if { |user| user['id'] == id }
|
280
|
+
memory['gamestates'].delete_if { |state| state['userid'] == id }
|
281
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
282
|
+
|
283
|
+
"User '#{id}' deleted successfully."
|
284
|
+
end
|
285
|
+
|
286
|
+
#
|
287
|
+
# API Endpoints for Game resources
|
288
|
+
#
|
289
|
+
|
290
|
+
#
|
291
|
+
# Lists all registered games.
|
292
|
+
#
|
293
|
+
# @return 200 OK, Response body includes JSON list of all registered games, list might be empty)
|
294
|
+
# Due to the fact that this request can be send unauthenticated, game data never!!! include data
|
295
|
+
# about users that are playing a game.
|
296
|
+
#
|
297
|
+
get "/games" do
|
298
|
+
games = memory['games']
|
299
|
+
JSON.pretty_generate(games)
|
300
|
+
end
|
301
|
+
|
302
|
+
#
|
303
|
+
# Creates a game.
|
304
|
+
#
|
305
|
+
# @param secret Secret of the game (used for authentication). Required. Parameter is part of request body.
|
306
|
+
# @param name Name of the game. Required. Parameter is part of request body.
|
307
|
+
# @param url URL of the game. Optional. Parameter is part of request body.
|
308
|
+
#
|
309
|
+
# @return 200 OK, on successfull creation (response body includes JSON representation of game)
|
310
|
+
# @return 400, on invalid url (response body includes error message)
|
311
|
+
# @return 400, on invalid name (response body includes error message)
|
312
|
+
# @return 409, if a game with provided name already exists (response body includes error message)
|
313
|
+
#
|
314
|
+
post "/game" do
|
315
|
+
name = params['name']
|
316
|
+
secret = params['secret']
|
317
|
+
url = params['url']
|
318
|
+
|
319
|
+
uri = URI.parse(url) rescue nil
|
320
|
+
|
321
|
+
if (name == nil || name.empty?)
|
322
|
+
status 400
|
323
|
+
return "Bad Request: '#{name}' is not a valid name"
|
324
|
+
end
|
325
|
+
|
326
|
+
if (uri != nil && !url.empty?)
|
327
|
+
if !uri.absolute?
|
328
|
+
status 400
|
329
|
+
return "Bad Request: '#{url}' is not a valid absolute url"
|
330
|
+
end
|
331
|
+
|
332
|
+
if !url =~ Defaults::VALID_URL_REGEX
|
333
|
+
status 400
|
334
|
+
return "Bad Request: '#{url}' is not a valid absolute url"
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
if memory['games'].map { |entity| entity['name'] }.include? name
|
339
|
+
status 409
|
340
|
+
return "Game with name '#{params['name']}' exists already."
|
341
|
+
end
|
342
|
+
|
343
|
+
id = SecureRandom.uuid
|
344
|
+
|
345
|
+
game = {
|
346
|
+
"type" => 'game',
|
347
|
+
"name" => params['name'],
|
348
|
+
"id" => id,
|
349
|
+
"url" => "#{uri}",
|
350
|
+
"signature" => Auth::signature(id, secret),
|
351
|
+
"created" => "#{ Time.now.utc.iso8601(6) }"
|
352
|
+
}
|
353
|
+
|
354
|
+
memory['games'] << game
|
355
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
356
|
+
JSON.pretty_generate(game)
|
357
|
+
end
|
358
|
+
|
359
|
+
#
|
360
|
+
# Retrieves game data.
|
361
|
+
#
|
362
|
+
# @param :id Unique identifier of the game (the id is never changed!). Required. Parameter is part of the REST-URI!
|
363
|
+
# @param secret Secret of the game (used for authentication). Required. Parameter is part of request body.
|
364
|
+
#
|
365
|
+
# @return 200 OK, response body includes JSON representation of user
|
366
|
+
# @return 401, if request is not provided with correct secret (response body includes error message)
|
367
|
+
# @return 404, if game with id is not present (response body includes error message)
|
368
|
+
#
|
369
|
+
get "/game/:id" do
|
370
|
+
|
371
|
+
secret = params['secret']
|
372
|
+
id = params['id']
|
373
|
+
game = service.get_game_by_id(id)
|
374
|
+
|
375
|
+
if game == nil
|
376
|
+
status 404
|
377
|
+
return "not found"
|
378
|
+
end
|
379
|
+
|
380
|
+
game = game.clone
|
381
|
+
|
382
|
+
unless Auth::authentic?(game, secret)
|
383
|
+
status 401
|
384
|
+
return "unauthorized, please provide correct credentials"
|
385
|
+
end
|
386
|
+
|
387
|
+
game['users'] = memory['gamestates'].select { |state| state['gameid'] == id }
|
388
|
+
.map { |state| state['userid'] }
|
389
|
+
.uniq
|
390
|
+
|
391
|
+
JSON.pretty_generate(game)
|
392
|
+
end
|
393
|
+
|
394
|
+
#
|
395
|
+
# Updates a game.
|
396
|
+
#
|
397
|
+
# @param :id Unique identifier of the game (the id is never changed!). Required. Parameter is part of the REST-URI!
|
398
|
+
# @param pwd Existing secret of the game (used for authentication). Required. Parameter is part of request body.
|
399
|
+
# @param new_name Changes name of the game to provided new name. Optional. Parameter is part of request body.
|
400
|
+
# @param new_mail Changes url of the game to provided new url. Optional. Parameter is part of request body.
|
401
|
+
# @param new_secret Changes secret of the game to a new secret. Optional. Parameter is part of request body.
|
402
|
+
#
|
403
|
+
# @return 200 OK, on successfull update (response body includes JSON representation of updated game)
|
404
|
+
# @return 400, on invalid url (response body includes error message)
|
405
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
406
|
+
# @return 409, on already existing new name (response body includes error message)
|
407
|
+
#
|
408
|
+
put "/game/:id" do
|
409
|
+
id = params['id']
|
410
|
+
secret = params['secret']
|
411
|
+
new_name = params['name']
|
412
|
+
new_url = params['url']
|
413
|
+
new_secret = params['newsecret']
|
414
|
+
|
415
|
+
uri = URI(new_url) rescue nil
|
416
|
+
|
417
|
+
if uri != nil && (!new_url =~ Defaults::VALID_URL_REGEX || !uri.absolute?)
|
418
|
+
status 400
|
419
|
+
return "Bad Request: '#{new_url}' is not a valid url."
|
420
|
+
end
|
421
|
+
|
422
|
+
if memory['games'].map { |entity| entity['name'] }.include? new_name
|
423
|
+
status 409
|
424
|
+
return "Game with name '#{new_name}' exists already."
|
425
|
+
end
|
426
|
+
|
427
|
+
begin
|
428
|
+
game = service.get_game_by_id(id)
|
429
|
+
|
430
|
+
unless Auth::authentic?(game, secret)
|
431
|
+
status 401
|
432
|
+
return "unauthorized, please provide correct credentials"
|
433
|
+
end
|
434
|
+
|
435
|
+
game['name'] = new_name if new_name != nil
|
436
|
+
game['url'] = new_url if new_url != nil
|
437
|
+
game['signature'] = Auth::signature(id, new_secret) if new_secret != nil
|
438
|
+
game['update'] = "#{ Time.now.utc.iso8601(6) }"
|
439
|
+
|
440
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
441
|
+
return JSON.pretty_generate(game)
|
442
|
+
rescue Exception => ex
|
443
|
+
status 401
|
444
|
+
return "#{ex}\nunauthorized, please provide correct credentials"
|
445
|
+
end
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
#
|
450
|
+
# Deletes a game and all of its associated game states.
|
451
|
+
#
|
452
|
+
# @param :id Unique identifier of the game (used for authentication). Required. Parameter is part of the REST-URI!
|
453
|
+
# @param secret Existing secret of the game (used for authentication). Required. Parameter is part of request body.
|
454
|
+
#
|
455
|
+
# @return 200 OK, on successfull delete (response body includes confirmation message)
|
456
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
457
|
+
#
|
458
|
+
delete "/game/:id" do
|
459
|
+
id = params['id']
|
460
|
+
secret = params['secret']
|
461
|
+
|
462
|
+
game = service.get_game_by_id(id)
|
463
|
+
|
464
|
+
if game == nil
|
465
|
+
# This is an idempotent operation.
|
466
|
+
return "Game '#{id}' deleted successfully."
|
467
|
+
end
|
468
|
+
|
469
|
+
unless Auth::authentic?(game, secret)
|
470
|
+
status 401
|
471
|
+
return "unauthorized, please provide correct credentials"
|
472
|
+
end
|
473
|
+
|
474
|
+
memory['games'].delete_if { |game| game['id'] == id }
|
475
|
+
memory['gamestates'].delete_if { |state| state['gameid'] == id }
|
476
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
477
|
+
|
478
|
+
"Game '#{id}' deleted successfully."
|
479
|
+
end
|
480
|
+
|
481
|
+
#
|
482
|
+
# API Endpoint for Gamestate resources
|
483
|
+
#
|
484
|
+
|
485
|
+
#
|
486
|
+
# Retrieves all gamestates stored for a game and a user.
|
487
|
+
# Gamestates are returned sorted by decreasing creation timestamps.
|
488
|
+
#
|
489
|
+
# @param :gameid Unique identifier of the game (used for authentication). Required. Parameter is part of the REST-URI!
|
490
|
+
# @param :userid Unique identifier of the user (used for authentication). Required. Parameter is part of the REST-URI!
|
491
|
+
# @param secret Secret of the game (used for authentication). Required. Parameter is part of request body.
|
492
|
+
#
|
493
|
+
# @return 200 OK, (response body includes confirmation message)
|
494
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
495
|
+
# @return 404, not found (in case of gameid or userid are not existing) (response body includes error message)
|
496
|
+
#
|
497
|
+
get "/gamestate/:gameid/:userid" do
|
498
|
+
|
499
|
+
gameid = params['gameid']
|
500
|
+
userid = params['userid']
|
501
|
+
secret = params['secret']
|
502
|
+
|
503
|
+
game = service.get_game_by_id(gameid)
|
504
|
+
user = service.get_user_by_id(userid)
|
505
|
+
|
506
|
+
if game == nil || user == nil
|
507
|
+
status 404
|
508
|
+
"not found"
|
509
|
+
end
|
510
|
+
|
511
|
+
unless Auth::authentic?(game, secret)
|
512
|
+
status 401
|
513
|
+
return "unauthorized, please provide correct game credentials"
|
514
|
+
end
|
515
|
+
|
516
|
+
states = memory['gamestates'].select do |state|
|
517
|
+
state['gameid'] == gameid && state['userid'] == userid
|
518
|
+
end
|
519
|
+
|
520
|
+
return JSON.pretty_generate(states.map { |state|
|
521
|
+
r = state.clone
|
522
|
+
r['gamename'] = service.get_game_by_id(r['gameid'])['name']
|
523
|
+
r['username'] = service.get_user_by_id(r['userid'])['name']
|
524
|
+
r
|
525
|
+
}.sort { |b, a| Time.parse(a['created']) <=> Time.parse(b['created']) })
|
526
|
+
end
|
527
|
+
|
528
|
+
#
|
529
|
+
# Retrieves all gamestates stored for a game.
|
530
|
+
# Gamestates are returned sorted by decreasing creation timestamps.
|
531
|
+
#
|
532
|
+
# @param :gameid Unique identifier of the game (used for authentication). Required. Parameter is part of the REST-URI!
|
533
|
+
# @param secret Secret of the game (used for authentication). Required. Parameter is part of request body.
|
534
|
+
#
|
535
|
+
# @return 200 OK, (response body includes confirmation message)
|
536
|
+
# @return 401, on non matching access credentials (response body includes error message)
|
537
|
+
# @return 404, not found (in case of gameid or userid are not existing) (response body includes error message)
|
538
|
+
#
|
539
|
+
get "/gamestate/:gameid" do
|
540
|
+
|
541
|
+
gameid = params['gameid']
|
542
|
+
secret = params['secret']
|
543
|
+
|
544
|
+
game = service.get_game_by_id(gameid)
|
545
|
+
|
546
|
+
if game == nil
|
547
|
+
status 404
|
548
|
+
"not found"
|
549
|
+
end
|
550
|
+
|
551
|
+
unless Auth::authentic?(game, secret)
|
552
|
+
status 401
|
553
|
+
return "unauthorized, please provide correct game credentials"
|
554
|
+
end
|
555
|
+
|
556
|
+
states = memory['gamestates'].select do |state|
|
557
|
+
state['gameid'] == gameid
|
558
|
+
end
|
559
|
+
|
560
|
+
return JSON.pretty_generate(states.map { |state|
|
561
|
+
r = state.clone
|
562
|
+
r['gamename'] = service.get_game_by_id(r['gameid'])['name']
|
563
|
+
r['username'] = service.get_user_by_id(r['userid'])['name']
|
564
|
+
r
|
565
|
+
}.sort { |b, a| Time.parse(a['created']) <=> Time.parse(b['created']) })
|
566
|
+
end
|
567
|
+
|
568
|
+
#
|
569
|
+
# Stores a gamestate for a game and a user.
|
570
|
+
#
|
571
|
+
# @param :gameid Unique identifier of the game (used for authentication). Required. Parameter is part of the REST-URI!
|
572
|
+
# @param :userid Unique identifier of the user (used for authentication). Required. Parameter is part of the REST-URI!
|
573
|
+
# @param secret Secret of the game (used for authentication). Required. Parameter is part of request body.
|
574
|
+
# @param state JSON encoded gamestate to store. Required.
|
575
|
+
#
|
576
|
+
# @return 200 OK (response body includes confirmation message)
|
577
|
+
# @return 400, Bad request, in case of gamestate was empty or not encoded as valid JSON (response body includes error message)
|
578
|
+
# @return 401, on non matching access credentials of game (response body includes error message)
|
579
|
+
# @return 404, not found (in case of gameid or userid are not existing) (response body includes error message)
|
580
|
+
#
|
581
|
+
post "/gamestate/:gameid/:userid" do
|
582
|
+
|
583
|
+
gameid = params['gameid']
|
584
|
+
userid = params['userid']
|
585
|
+
secret = params['secret']
|
586
|
+
state = params['state']
|
587
|
+
|
588
|
+
game = service.get_game_by_id(gameid)
|
589
|
+
user = service.get_user_by_id(userid)
|
590
|
+
|
591
|
+
unless game != nil && user != nil
|
592
|
+
status 404
|
593
|
+
return "game id or user id not found"
|
594
|
+
end
|
595
|
+
|
596
|
+
unless Auth::authentic?(game, secret)
|
597
|
+
status 401
|
598
|
+
return "unauthorized, please provide correct game credentials"
|
599
|
+
end
|
600
|
+
|
601
|
+
begin
|
602
|
+
state = JSON.parse(state)
|
603
|
+
|
604
|
+
if state.empty?
|
605
|
+
status 400
|
606
|
+
return "Bad request: state must not be empty, was #{state}"
|
607
|
+
end
|
608
|
+
|
609
|
+
memory['gamestates'] << {
|
610
|
+
"type" => 'gamestate',
|
611
|
+
"gameid" => gameid,
|
612
|
+
"userid" => userid,
|
613
|
+
"created" => "#{ Time.now.utc.iso8601(6) }",
|
614
|
+
"state" => state
|
615
|
+
}
|
616
|
+
|
617
|
+
rescue
|
618
|
+
status 400
|
619
|
+
return "Bad request: state must be provided as valid JSON, was #{state}"
|
620
|
+
end
|
621
|
+
|
622
|
+
File.open(storage, "w") { |file| file.write(JSON.pretty_generate(memory)) }
|
623
|
+
|
624
|
+
"Added state."
|
625
|
+
|
626
|
+
end
|
627
|
+
|
628
|
+
end
|
629
|
+
|
630
|
+
end
|
631
|
+
|
632
|
+
end
|