Class: Saro::Dat::DatManager
- Inherits:
-
Object
- Object
- Saro::Dat::DatManager
- Defined in:
- lib/saro/dat/dat_manager.rb
Defined Under Namespace
Classes: State
Class Method Summary collapse
-
._issue(cert, plain, secure) ⇒ Object
NOTE: 아래
private는def self.메서드에 적용되지 않는다. - ._parse(cert, dat_input) ⇒ Object
Instance Method Summary collapse
- #exports(verify_only = false) ⇒ Object
- #import_certificates(input_certs, clear: false) ⇒ Object
- #imports(format_str, clear: false) ⇒ Object
-
#initialize ⇒ DatManager
constructor
A new instance of DatManager.
- #issue(plain, secure) ⇒ Object
- #parse(dat_input) ⇒ Object
Constructor Details
#initialize ⇒ DatManager
Returns a new instance of DatManager.
21 22 23 24 |
# File 'lib/saro/dat/dat_manager.rb', line 21 def initialize @state = EMPTY_STATE @write_lock = Mutex.new end |
Class Method Details
._issue(cert, plain, secure) ⇒ Object
NOTE: 아래 private 는 def self. 메서드에 적용되지 않는다. _issue/_parse 는
예전부터 실제로는 public 이었고 테스트·벤치가 그렇게 쓰고 있다. 오해를 없애려
위치를 옮기고, 정말 감춰야 하는 것만 private_class_method 로 막는다.
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/saro/dat/dat_manager.rb', line 155 def self._issue(cert, plain, secure) now = Time.now.to_i expire = now + cert.dat_ttl_seconds cid_hex = cert.cid.to_s(16) plain_b64 = Saro::Dat::Util.encode_base64_url_str(plain) encrypted_secure = cert.crypto_key.encrypt(secure) secure_b64 = Saro::Dat::Util.encode_base64_url_str(encrypted_secure) body = "#{expire}.#{cid_hex}.#{plain_b64}.#{secure_b64}" signature = Saro::Dat::Util.encode_base64_url_str(cert.signature_key.sign(body)) "#{body}.#{signature}" end |
._parse(cert, dat_input) ⇒ Object
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/saro/dat/dat_manager.rb', line 171 def self._parse(cert, dat_input) dat = Saro::Dat::Dat.from_value(dat_input) # 같은 조건이 여기서는 RuntimeError, DatManager#parse 에서는 ArgumentError # 였다. 이제 양쪽 모두 같은 코드를 던진다. dat.raise_if_invalid! raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::TOKEN_EXPIRED) if dat.expired? # verify 는 불일치일 때만 false 를 준다. 연산 실패는 SIG_BACKEND 로 올라온다. unless cert.signature_key.verify(dat.body_string, dat.signature) raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::SIG_MISMATCH) end decrypted_secure = cert.crypto_key.decrypt(dat.secure) Saro::Dat::DatPayload.new(dat.plain, decrypted_secure) end |
Instance Method Details
#exports(verify_only = false) ⇒ Object
82 83 84 |
# File 'lib/saro/dat/dat_manager.rb', line 82 def exports(verify_only = false) @state.certificates.map { |cert| cert.exports(verify_only) }.join("\n") end |
#import_certificates(input_certs, clear: false) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/saro/dat/dat_manager.rb', line 26 def import_certificates(input_certs, clear: false) # rust returns early on an empty input without touching the state, so an # empty CMS response can never wipe the certificates held by a manager # that is importing with clear: true. return 0 if input_certs.nil? || input_certs.empty? # Duplicate detection runs before any mutation (rust checks the whole # input up front), so a bad payload cannot leave a half-applied state. seen_cids = Set.new input_certs.each do |cert| if seen_cids.include?(cert.cid) raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::CERT_DUPLICATE_CID, "duplicate cid #{cert.cid.to_s(16)}") end seen_cids.add(cert.cid) end renew_count = 0 @write_lock.synchronize do certificates = clear ? [] : @state.certificates.dup cids = Set.new(certificates.map(&:cid)) input_certs.each do |cert| next if cids.include?(cert.cid) cids.add(cert.cid) certificates << cert renew_count += 1 end # Expired certificates are dropped from the *merged* list, not just # from the incoming one, so a renewal sweeps out what has aged out. certificates.reject!(&:expired) certificates.sort_by!(&:dat_issuance_end_seconds) # Find latest issuable certificate as issuer issuer = certificates.reverse_each.find(&:issuable) by_cid = {} certificates.each { |cert| by_cid[cert.cid] = cert } @state = State.new(issuer, certificates.freeze, by_cid.freeze).freeze end renew_count end |
#imports(format_str, clear: false) ⇒ Object
72 73 74 75 76 77 78 79 80 |
# File 'lib/saro/dat/dat_manager.rb', line 72 def imports(format_str, clear: false) certs = [] format_str.strip.split("\n").each do |line| line = line.strip next if line.empty? certs << Saro::Dat::DatCertificate.imports(line) end import_certificates(certs, clear: clear) end |
#issue(plain, secure) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/saro/dat/dat_manager.rb', line 86 def issue(plain, secure) state = @state issuer = state.issuer unless issuer # 예전에는 이 다섯 가지가 "Invalid DAT: Signing Key Does Not Exist" # 문자열 하나였다. 대응이 전부 다르다 — 발급창 전이면 기다리면 되고, # verify-only 뿐이면 배포 설정 실수이며, 0건이면 CMS 접속 문제다. raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::MANAGER_NO_CERTIFICATE) if state.certificates.empty? raise Saro::Dat::Error.new( Saro::Dat::ErrorCode::MANAGER_NO_ISSUABLE_CERTIFICATE, cause: no_issuable_cause(state.certificates) ) end self.class._issue(issuer, plain, secure) end |
#parse(dat_input) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/saro/dat/dat_manager.rb', line 103 def parse(dat_input) dat = Saro::Dat::Dat.from_value(dat_input) # 파싱 실패의 코드를 그대로 올린다. dat.raise_if_invalid! # 만료를 cid 조회보다 먼저 본다. 기준 구현(rust)이 토큰을 읽는 시점에 # 만료를 판정하므로, 모르는 cid 의 만료 토큰도 만료로 보고된다. raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::TOKEN_EXPIRED) if dat.expired? certificate = @state.by_cid[dat.cid] unless certificate raise Saro::Dat::Error.new(Saro::Dat::ErrorCode::CERT_NOT_FOUND, "cid #{dat.cid.to_s(16)}") end self.class._parse(certificate, dat) end |