magister 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/magister/authenticator.rb +73 -0
- data/lib/magister/data.rb +22 -0
- data/lib/magister/profile.rb +82 -0
- data/lib/magister.rb +31 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8615e0de52da0b061810bd3ead80cb1be7c5c74b4cd019fa8adc378c2ed5ab3d
|
4
|
+
data.tar.gz: a3c20cfb453481be6079f14878e00e8d70ffe3c4b56769c5df0c79535547dd7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a00609ba584cf34ccd127aa7f3d431856bf1e223e2bba6b7dafc3d752bca0972ac30664d8befa0a2e4db831678b684fdfaa1fa34e368f86c8dae4fb44a3f44ed
|
7
|
+
data.tar.gz: 68e5d06e95b52ebaad3c33ce98266d2e1967eb63f0e5efdce94dda66eaeae538290360a8273ae0596a3e10f01d9cf5eb77dd4c711f3127756d193c2302f43d15
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require './lib/magister/profile.rb'
|
5
|
+
require 'net/http'
|
6
|
+
require 'json'
|
7
|
+
require 'securerandom'
|
8
|
+
require 'base64'
|
9
|
+
require 'digest'
|
10
|
+
require 'selenium-webdriver'
|
11
|
+
|
12
|
+
module Authenticator
|
13
|
+
extend self
|
14
|
+
|
15
|
+
def login(username, password, school)
|
16
|
+
# uri = URI("https://#{school}.magister.net/oidc_config.js")
|
17
|
+
# http = Net::HTTP.new(uri.host, uri.port)
|
18
|
+
# http.use_ssl = true
|
19
|
+
# request = Net::HTTP::Get.new(uri.request_uri)
|
20
|
+
# response = http.request(request)
|
21
|
+
# oidc_conf = (response.body.split("config =").last.split("};").first.gsub("window.location.hostname", "'" + uri.hostname + "'") + "}").gsub(': ', '":').gsub(/,(\s*)/, ',"').gsub(/{(\s*)/, '{"').gsub("'", '"').gsub('" + "', "")
|
22
|
+
|
23
|
+
# oidc_conf = JSON.parse(oidc_conf)
|
24
|
+
|
25
|
+
codeVerifier = SecureRandom.alphanumeric(128)
|
26
|
+
verifier = Base64.urlsafe_encode64(codeVerifier)
|
27
|
+
|
28
|
+
rawChallenge = Digest::SHA256.hexdigest verifier
|
29
|
+
challenge = Base64.urlsafe_encode64(rawChallenge)
|
30
|
+
|
31
|
+
@@state = SecureRandom.hex(16)
|
32
|
+
@@nonce = SecureRandom.hex(16)
|
33
|
+
|
34
|
+
auth_uri = "https://#{school}.magister.net/connect/authorize?client_id=M6LOAPP&redirect_uri=m6loapp%3A%2F%2Foauth2redirect%2F&scope=openid%20profile%20opp.read%20opp.manage%20attendance.overview%20attendance.administration%20calendar.ical.user%20calendar.to-do.user%20grades.read%20grades.manage&state=#{@@state}&nonce=#{@@nonce}&code_challenge=#{challenge}&code_challenge_method=S256&prompt=select_account"
|
35
|
+
# puts "using authentication url #{auth_uri}"
|
36
|
+
|
37
|
+
if $authMode == "local"
|
38
|
+
raise NotImplementedError.new("\n\nLocal authentication mode has not been implemented yet, \nCheck our github for any updates, or if you want to help implementing it!\n")
|
39
|
+
else
|
40
|
+
puts "Using Selenium with Chrome for authentication."
|
41
|
+
options = Selenium::WebDriver::Options.chrome(args: ['--headless=new'])
|
42
|
+
driver = Selenium::WebDriver.for :chrome, options: options
|
43
|
+
|
44
|
+
driver.get auth_uri
|
45
|
+
while !driver.current_url.start_with? "https://accounts.magister.net/account/login"
|
46
|
+
sleep(0.5)
|
47
|
+
# puts "waiting for load..."
|
48
|
+
end
|
49
|
+
sleep(3)
|
50
|
+
|
51
|
+
username_field = driver.find_element(id: 'username')
|
52
|
+
username_field.send_keys(username)
|
53
|
+
go_to_password_button = driver.find_element(id: 'username_submit')
|
54
|
+
go_to_password_button.click
|
55
|
+
|
56
|
+
sleep(1)
|
57
|
+
|
58
|
+
password_field = driver.find_element(id: 'password')
|
59
|
+
password_field.send_keys(password)
|
60
|
+
signin_button = driver.find_element(id: 'password_submit')
|
61
|
+
signin_button.click
|
62
|
+
|
63
|
+
wait = Selenium::WebDriver::Wait.new(timeout: 30)
|
64
|
+
wait.until { driver.current_url.start_with? "https://#{school}.magister.net/oidc/redirect_callback.html" }
|
65
|
+
|
66
|
+
token = driver.current_url.split("access_token=").last.split("&").first
|
67
|
+
|
68
|
+
driver.quit
|
69
|
+
|
70
|
+
return Profile.new(token, school)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require 'date'
|
5
|
+
|
6
|
+
def validate_date(date)
|
7
|
+
format = '%Y-%m-%d'
|
8
|
+
DateTime.strptime(date, format)
|
9
|
+
true
|
10
|
+
rescue ArgumentError
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
module MagisterData
|
15
|
+
def get_classes(dateFrom, dateTo)
|
16
|
+
if validate_date(dateFrom) && validate_date(dateTo)
|
17
|
+
@profile.authenticatedRequest("/personen/{*id*}/afspraken?status=1&van=#{dateFrom}&tot=#{dateTo}")
|
18
|
+
else
|
19
|
+
puts "Invalid date, Format is yyyy-mm-dd"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require 'uri'
|
5
|
+
require 'net/http'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class Profile
|
9
|
+
def initialize(token, school)
|
10
|
+
@token = token
|
11
|
+
@id = 0
|
12
|
+
@school = school
|
13
|
+
@person = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def token=(token)
|
17
|
+
@token=token
|
18
|
+
end
|
19
|
+
def id
|
20
|
+
@id
|
21
|
+
end
|
22
|
+
def school
|
23
|
+
@school
|
24
|
+
end
|
25
|
+
def inspect
|
26
|
+
"#<#{self.class}:0x#{object_id} @token=\"[PRIVATE]\", @id=#{@id}, @school=#{@school}>"
|
27
|
+
end
|
28
|
+
|
29
|
+
def person
|
30
|
+
@person
|
31
|
+
end
|
32
|
+
|
33
|
+
def verify()
|
34
|
+
if @school == nil || @token == nil
|
35
|
+
puts "Either school or token was not defined!"
|
36
|
+
end
|
37
|
+
puts "authenticating to #{@school}..."
|
38
|
+
uri = URI("https://#{@school}.magister.net/api/account")
|
39
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
40
|
+
http.use_ssl = true
|
41
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
42
|
+
request['authorization'] = "Bearer #{@token}"
|
43
|
+
|
44
|
+
begin
|
45
|
+
response = http.request(request)
|
46
|
+
if response.is_a?(Net::HTTPSuccess)
|
47
|
+
res = JSON.parse(response.body)
|
48
|
+
@person = res["Persoon"]
|
49
|
+
@id = person["Id"].to_i
|
50
|
+
puts "Succesfully authenticated as #{@person["Roepnaam"]} with id #{@person["Id"]}"
|
51
|
+
else
|
52
|
+
puts "Failed to authenticate, http code #{response.code}"
|
53
|
+
end
|
54
|
+
rescue StandardError => e
|
55
|
+
puts "An error occurred #{e}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def authenticatedRequest(endpoint)
|
60
|
+
if @id == 0
|
61
|
+
puts "Not yet authenticated!"
|
62
|
+
self.verify()
|
63
|
+
end
|
64
|
+
uri = URI("https://#{@school}.magister.net/api#{endpoint}".gsub("{*id*}", @id.to_s))
|
65
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
66
|
+
http.use_ssl = true
|
67
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
68
|
+
request['authorization'] = "Bearer #{@token}"
|
69
|
+
|
70
|
+
begin
|
71
|
+
response = http.request(request)
|
72
|
+
if response.is_a?(Net::HTTPSuccess)
|
73
|
+
return JSON.parse(response.body)
|
74
|
+
else
|
75
|
+
puts "failed to get #{endpoint}, status code #{response.code}"
|
76
|
+
return nil
|
77
|
+
end
|
78
|
+
rescue StandardError => e
|
79
|
+
puts "An error occurred #{e}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/magister.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
3
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
4
|
+
require './lib/magister/profile.rb'
|
5
|
+
require './lib/magister/data.rb'
|
6
|
+
require './lib/magister/authenticator.rb'
|
7
|
+
|
8
|
+
# can be either "selenium" or "local"
|
9
|
+
# "selenium" will run headlessly
|
10
|
+
# "local" will open a browser window for the user to login
|
11
|
+
$authMode = "selenium"
|
12
|
+
|
13
|
+
class Magister
|
14
|
+
include MagisterData
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@profile = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def authenticate(school, token)
|
21
|
+
@profile = Profile.new(token, school)
|
22
|
+
@profile.verify
|
23
|
+
end
|
24
|
+
def login(school, username, password)
|
25
|
+
@profile = Authenticator.login(username, password, school)
|
26
|
+
end
|
27
|
+
|
28
|
+
def profile
|
29
|
+
@profile
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: magister
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Riley0122
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.7'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.7.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.7'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.7.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: securerandom
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.3.1
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.3.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: base64
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.2.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.2.0
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: selenium-webdriver
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4.17'
|
68
|
+
type: :runtime
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '4.17'
|
75
|
+
description: An unofficial Magister API wrapper for ruby
|
76
|
+
email: contact@riley0122.dev
|
77
|
+
executables: []
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- lib/magister.rb
|
82
|
+
- lib/magister/authenticator.rb
|
83
|
+
- lib/magister/data.rb
|
84
|
+
- lib/magister/profile.rb
|
85
|
+
homepage: https://github.com/riley0122/rubymag#readme
|
86
|
+
licenses:
|
87
|
+
- MPL-2.0
|
88
|
+
metadata:
|
89
|
+
source_code_uri: https://github.com/riley0122/rubymag
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubygems_version: 3.4.10
|
106
|
+
signing_key:
|
107
|
+
specification_version: 4
|
108
|
+
summary: Magister API wrapper
|
109
|
+
test_files: []
|