Fix issues with crypting

multistore
Laurent 2 years ago
parent 86a1fe3871
commit 45c3d25299
  1. 386
      PadelClub/Data/Tournament.swift
  2. 6
      PadelClub/Views/Subscription/Guard.swift
  3. 6
      PadelClub/Views/Tournament/Screen/Components/TournamentStatusView.swift
  4. 2
      PadelClub/Views/Tournament/TournamentView.swift

@ -41,14 +41,50 @@ class Tournament : ModelObject, Storable {
var qualifiedPerGroupStage: Int
var teamsPerGroupStage: Int
var entryFee: Double?
var payment: Data? = nil
var payment: TournamentPayment? = nil
var additionalEstimationDuration: Int = 0
var isDeleted: Bool = false
var isCanceled: Data? = nil
var isCanceled: Bool = false
@ObservationIgnored
var navigationPath: [Screen] = []
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _creator = "creator"
case _name = "name"
case _startDate = "startDate"
case _endDate = "endDate"
case _creationDate = "creationDate"
case _isPrivate = "isPrivate"
case _groupStageFormat = "groupStageFormat"
case _roundFormat = "roundFormat"
case _loserRoundFormat = "loserRoundFormat"
case _groupStageSortMode = "groupStageSortMode"
case _groupStageCount = "groupStageCount"
case _rankSourceDate = "rankSourceDate"
case _dayDuration = "dayDuration"
case _teamCount = "teamCount"
case _teamSorting = "teamSorting"
case _federalCategory = "federalCategory"
case _federalLevelCategory = "federalLevelCategory"
case _federalAgeCategory = "federalAgeCategory"
case _groupStageCourtCount = "groupStageCourtCount"
case _seedCount = "seedCount"
case _closedRegistrationDate = "closedRegistrationDate"
case _groupStageAdditionalQualified = "groupStageAdditionalQualified"
case _courtCount = "courtCount"
case _prioritizeClubMembers = "prioritizeClubMembers"
case _qualifiedPerGroupStage = "qualifiedPerGroupStage"
case _teamsPerGroupStage = "teamsPerGroupStage"
case _entryFee = "entryFee"
case _additionalEstimationDuration = "additionalEstimationDuration"
case _isDeleted = "isDeleted"
case _isCanceled = "localId"
case _payment = "globalId"
}
internal init(event: String? = nil, creator: String? = nil, name: String? = nil, startDate: Date = Date(), endDate: Date? = nil, creationDate: Date = Date(), isPrivate: Bool = true, groupStageFormat: MatchFormat? = nil, roundFormat: MatchFormat? = nil, loserRoundFormat: MatchFormat? = nil, groupStageSortMode: GroupStageOrderingMode, groupStageCount: Int = 4, rankSourceDate: Date? = nil, dayDuration: Int = 1, teamCount: Int = 24, teamSorting: TeamSortingType? = nil, federalCategory: TournamentCategory, federalLevelCategory: TournamentLevel, federalAgeCategory: FederalTournamentAge, groupStageCourtCount: Int? = nil, seedCount: Int = 8, closedRegistrationDate: Date? = nil, groupStageAdditionalQualified: Int = 0, courtCount: Int = 2, prioritizeClubMembers: Bool = false, qualifiedPerGroupStage: Int = 1, teamsPerGroupStage: Int = 4, entryFee: Double? = nil) {
self.event = event
self.creator = creator
@ -80,6 +116,150 @@ class Tournament : ModelObject, Storable {
self.entryFee = entryFee
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: ._id)
event = try container.decodeIfPresent(String.self, forKey: ._event)
creator = try container.decodeIfPresent(String.self, forKey: ._creator)
name = try container.decodeIfPresent(String.self, forKey: ._name)
startDate = try container.decode(Date.self, forKey: ._startDate)
endDate = try container.decodeIfPresent(Date.self, forKey: ._endDate)
creationDate = try container.decode(Date.self, forKey: ._creationDate)
isPrivate = try container.decode(Bool.self, forKey: ._isPrivate)
groupStageFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._groupStageFormat)
roundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._roundFormat)
loserRoundFormat = try container.decodeIfPresent(MatchFormat.self, forKey: ._loserRoundFormat)
groupStageSortMode = try container.decode(GroupStageOrderingMode.self, forKey: ._groupStageSortMode)
groupStageCount = try container.decode(Int.self, forKey: ._groupStageCount)
rankSourceDate = try container.decodeIfPresent(Date.self, forKey: ._rankSourceDate)
dayDuration = try container.decode(Int.self, forKey: ._dayDuration)
teamCount = try container.decode(Int.self, forKey: ._teamCount)
teamSorting = try container.decode(TeamSortingType.self, forKey: ._teamSorting)
federalCategory = try container.decode(TournamentCategory.self, forKey: ._federalCategory)
federalLevelCategory = try container.decode(TournamentLevel.self, forKey: ._federalLevelCategory)
federalAgeCategory = try container.decode(FederalTournamentAge.self, forKey: ._federalAgeCategory)
groupStageCourtCount = try container.decodeIfPresent(Int.self, forKey: ._groupStageCourtCount)
seedCount = try container.decode(Int.self, forKey: ._seedCount)
closedRegistrationDate = try container.decodeIfPresent(Date.self, forKey: ._closedRegistrationDate)
groupStageAdditionalQualified = try container.decode(Int.self, forKey: ._groupStageAdditionalQualified)
courtCount = try container.decode(Int.self, forKey: ._courtCount)
prioritizeClubMembers = try container.decode(Bool.self, forKey: ._prioritizeClubMembers)
qualifiedPerGroupStage = try container.decode(Int.self, forKey: ._qualifiedPerGroupStage)
teamsPerGroupStage = try container.decode(Int.self, forKey: ._teamsPerGroupStage)
entryFee = try container.decodeIfPresent(Double.self, forKey: ._entryFee)
payment = try Tournament._decodePayment(container: container)
additionalEstimationDuration = try container.decode(Int.self, forKey: ._additionalEstimationDuration)
isDeleted = try container.decode(Bool.self, forKey: ._isDeleted)
isCanceled = try Tournament._decodeCanceled(container: container)
}
fileprivate static let _numberFormatter: NumberFormatter = NumberFormatter()
fileprivate static func _decodePayment(container: KeyedDecodingContainer<CodingKeys>) throws -> TournamentPayment? {
let data = try container.decodeIfPresent(Data.self, forKey: ._payment)
if let data {
do {
let decoded: String = try data.decryptData(pass: Key.pass.rawValue)
let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
return TournamentPayment(rawValue: sequence[18])
} catch {
Logger.error(error)
}
}
return nil
}
fileprivate static func _decodeCanceled(container: KeyedDecodingContainer<CodingKeys>) throws -> Bool {
let data = try container.decodeIfPresent(Data.self, forKey: ._isCanceled)
if let data {
do {
let decoded: String = try data.decryptData(pass: Key.pass.rawValue)
let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
return Bool.decodeInt(sequence[18])
} catch {
Logger.error(error)
}
}
return false
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: ._id)
try container.encodeIfPresent(event, forKey: ._event)
try container.encodeIfPresent(creator, forKey: ._creator)
try container.encodeIfPresent(name, forKey: ._name)
try container.encode(startDate, forKey: ._startDate)
try container.encodeIfPresent(endDate, forKey: ._endDate)
try container.encode(creationDate, forKey: ._creationDate)
try container.encode(isPrivate, forKey: ._isPrivate)
try container.encodeIfPresent(groupStageFormat, forKey: ._groupStageFormat)
try container.encodeIfPresent(roundFormat, forKey: ._roundFormat)
try container.encodeIfPresent(loserRoundFormat, forKey: ._loserRoundFormat)
try container.encode(groupStageSortMode, forKey: ._groupStageSortMode)
try container.encode(groupStageCount, forKey: ._groupStageCount)
try container.encodeIfPresent(rankSourceDate, forKey: ._rankSourceDate)
try container.encode(dayDuration, forKey: ._dayDuration)
try container.encode(teamCount, forKey: ._teamCount)
try container.encode(teamSorting, forKey: ._teamSorting)
try container.encode(federalCategory, forKey: ._federalCategory)
try container.encode(federalLevelCategory, forKey: ._federalLevelCategory)
try container.encode(federalAgeCategory, forKey: ._federalAgeCategory)
try container.encodeIfPresent(groupStageCourtCount, forKey: ._groupStageCourtCount)
try container.encode(seedCount, forKey: ._seedCount)
try container.encodeIfPresent(closedRegistrationDate, forKey: ._closedRegistrationDate)
try container.encode(groupStageAdditionalQualified, forKey: ._groupStageAdditionalQualified)
try container.encode(courtCount, forKey: ._courtCount)
try container.encode(prioritizeClubMembers, forKey: ._prioritizeClubMembers)
try container.encode(qualifiedPerGroupStage, forKey: ._qualifiedPerGroupStage)
try container.encode(teamsPerGroupStage, forKey: ._teamsPerGroupStage)
try container.encodeIfPresent(entryFee, forKey: ._entryFee)
try self._encodePayment(container: &container)
try container.encode(additionalEstimationDuration, forKey: ._additionalEstimationDuration)
try container.encode(isDeleted, forKey: ._isDeleted)
try self._encodeIsCanceled(container: &container)
}
fileprivate func _encodePayment(container: inout KeyedEncodingContainer<CodingKeys>) throws {
guard let payment else {
try container.encodeNil(forKey: ._payment)
return
}
let max: Int = TournamentPayment.allCases.count
var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
sequence.append(payment.rawValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
let encryped: Data = try data.encrypt(pass: Key.pass.rawValue)
try container.encodeIfPresent(encryped, forKey: ._payment)
}
}
func _encodeIsCanceled(container: inout KeyedEncodingContainer<CodingKeys>) throws {
let max: Int = 9
var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
sequence.append(self.isCanceled.encodedValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
let encryped: Data = try data.encrypt(pass: Key.pass.rawValue)
try container.encode(encryped, forKey: ._isCanceled)
}
}
/// Warning: if the enum has more than 10 cases, the payment algo is broken
enum TournamentPayment: Int, CaseIterable {
case free, unit, subscriptionUnit, unlimited
@ -150,9 +330,9 @@ class Tournament : ModelObject, Storable {
}
func state() -> Tournament.State {
// if currentCanceled == true {
// return .canceled
// }
if self.isCanceled == true {
return .canceled
}
if (groupStageCount > 0 && groupStages().isEmpty == false)
|| rounds().isEmpty == false {
return .build
@ -1150,96 +1330,94 @@ class Tournament : ModelObject, Storable {
fileprivate var _currentPayment: TournamentPayment? = nil
fileprivate var _currentCanceled: Bool? = nil
fileprivate let _numberFormatter: NumberFormatter = NumberFormatter()
func setPayment(_ payment: TournamentPayment) {
let max: Int = TournamentPayment.allCases.count
self._currentPayment = payment
var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
sequence.append(payment.rawValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
do {
self.payment = try data.encrypt(pass: Key.pass.rawValue)
} catch {
Logger.error(error)
}
}
}
var currentPayment: TournamentPayment? {
if let current = self._currentPayment {
return current
}
self._currentPayment = self.decryptPayment()
return self._currentPayment
}
func decryptPayment() -> TournamentPayment? {
if let payment {
do {
let decoded: String = try payment.decryptData(pass: Key.pass.rawValue)
let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
return TournamentPayment(rawValue: sequence[18])
} catch {
Logger.error(error)
}
}
return nil
}
func setCanceled(_ canceled: Bool) {
let max: Int = 9
self._currentCanceled = canceled
var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
sequence.append(canceled.encodedValue)
sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
let stringCombo: [String] = sequence.map { $0.formatted() }
let joined: String = stringCombo.joined(separator: "")
if let data = joined.data(using: .utf8) {
do {
self.isCanceled = try data.encrypt(pass: Key.pass.rawValue)
} catch {
Logger.error(error)
}
}
}
// func setPayment(_ payment: TournamentPayment) {
//
// let max: Int = TournamentPayment.allCases.count
// self._currentPayment = payment
// var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
// sequence.append(payment.rawValue)
// sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
//
// let stringCombo: [String] = sequence.map { $0.formatted() }
// let joined: String = stringCombo.joined(separator: "")
// if let data = joined.data(using: .utf8) {
// do {
// self.payment = try data.encrypt(pass: Key.pass.rawValue)
// } catch {
// Logger.error(error)
// }
// }
// }
var currentCanceled: Bool? {
if let current = self._currentCanceled {
return current
}
self._currentCanceled = self.decryptCanceled()
return self._currentCanceled
}
// var currentPayment: TournamentPayment? {
// if let current = self._currentPayment {
// return current
// }
// self._currentPayment = self.decryptPayment()
// return self._currentPayment
// }
// func decryptPayment() -> TournamentPayment? {
// if let payment {
// do {
// let decoded: String = try payment.decryptData(pass: Key.pass.rawValue)
// let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
// return TournamentPayment(rawValue: sequence[18])
// } catch {
// Logger.error(error)
// }
// }
// return nil
// }
// func setCanceled(_ canceled: Bool) {
//
// let max: Int = 9
// self._currentCanceled = canceled
// var sequence = (1...18).map { _ in Int.random(in: (0..<max)) }
// sequence.append(canceled.encodedValue)
// sequence.append(contentsOf: (1...13).map { _ in Int.random(in: (0..<max ))} )
//
// let stringCombo: [String] = sequence.map { $0.formatted() }
// let joined: String = stringCombo.joined(separator: "")
// if let data = joined.data(using: .utf8) {
// do {
// self.isCanceled = try data.encrypt(pass: Key.pass.rawValue)
// } catch {
// Logger.error(error)
// }
// }
// }
func decryptCanceled() -> Bool? {
if let isCanceled {
do {
let decoded: String = try isCanceled.decryptData(pass: Key.pass.rawValue)
let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
return Bool.decodeInt(sequence[18])
} catch {
Logger.error(error)
}
}
return nil
}
// var currentCanceled: Bool? {
// if let current = self._currentCanceled {
// return current
// }
// self._currentCanceled = self.decryptCanceled()
// return self._currentCanceled
// }
// func decryptCanceled() -> Bool? {
// if let isCanceled {
// do {
// let decoded: String = try isCanceled.decryptData(pass: Key.pass.rawValue)
// let sequence = decoded.compactMap { _numberFormatter.number(from: String($0))?.intValue }
// return Bool.decodeInt(sequence[18])
// } catch {
// Logger.error(error)
// }
// }
// return nil
// }
enum PaymentError: Error {
case cantPayTournament
}
func payIfNecessary() throws {
if self.currentPayment != nil { return }
if let payment = Guard.main.paymentForNewTournament() {
self.setPayment(payment)
self.payment = payment
try DataStore.shared.tournaments.addOrUpdate(instance: self)
return
}
@ -1267,44 +1445,6 @@ fileprivate extension Bool {
}
}
extension Tournament {
enum CodingKeys: String, CodingKey {
case _id = "id"
case _event = "event"
case _creator = "creator"
case _name = "name"
case _startDate = "startDate"
case _endDate = "endDate"
case _creationDate = "creationDate"
case _isPrivate = "isPrivate"
case _groupStageFormat = "groupStageFormat"
case _roundFormat = "roundFormat"
case _loserRoundFormat = "loserRoundFormat"
case _groupStageSortMode = "groupStageSortMode"
case _groupStageCount = "groupStageCount"
case _rankSourceDate = "rankSourceDate"
case _dayDuration = "dayDuration"
case _teamCount = "teamCount"
case _teamSorting = "teamSorting"
case _federalCategory = "federalCategory"
case _federalLevelCategory = "federalLevelCategory"
case _federalAgeCategory = "federalAgeCategory"
case _groupStageCourtCount = "groupStageCourtCount"
case _seedCount = "seedCount"
case _closedRegistrationDate = "closedRegistrationDate"
case _groupStageAdditionalQualified = "groupStageAdditionalQualified"
case _courtCount = "courtCount"
case _prioritizeClubMembers = "prioritizeClubMembers"
case _qualifiedPerGroupStage = "qualifiedPerGroupStage"
case _teamsPerGroupStage = "teamsPerGroupStage"
case _entryFee = "entryFee"
case _additionalEstimationDuration = "additionalEstimationDuration"
case _isDeleted = "isDeleted"
case _isCanceled = "localId"
case _payment = "globalId"
}
}
extension Tournament: Hashable {
static func == (lhs: Tournament, rhs: Tournament) -> Bool {
lhs.id == rhs.id

@ -178,7 +178,7 @@ import LeStorage
return Tournament.TournamentPayment.unlimited
case .fivePerMonth:
if let purchaseDate = self.currentBestPlan?.originalPurchaseDate {
let tournaments = DataStore.shared.tournaments.filter { $0.creationDate > purchaseDate && $0.currentCanceled == false }
let tournaments = DataStore.shared.tournaments.filter { $0.creationDate > purchaseDate && $0.isCanceled == false }
if tournaments.count < StoreItem.five {
return Tournament.TournamentPayment.subscriptionUnit
}
@ -187,7 +187,7 @@ import LeStorage
default:
// let subscriptionPayed = DataStore.shared.tournaments.filter { $0.payment?.isSubscription == true }
let unitlyPayed = DataStore.shared.tournaments.filter { $0.currentPayment == .unit && $0.currentCanceled == false }.count
let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == .unit && $0.isCanceled == false }.count
if unitlyPayed == 0 {
return Tournament.TournamentPayment.free
}
@ -201,7 +201,7 @@ import LeStorage
}
var remainingTournaments: Int {
let unitlyPayed = DataStore.shared.tournaments.filter { $0.currentPayment == Tournament.TournamentPayment.unit }.count
let unitlyPayed = DataStore.shared.tournaments.filter { $0.payment == Tournament.TournamentPayment.unit }.count
let tournamentCreditCount = self._purchasedTournamentCount()
Logger.log("total count = \(DataStore.shared.tournaments.count), unitlyPayed = \(unitlyPayed), purchased = \(tournamentCreditCount) ")
return tournamentCreditCount - unitlyPayed

@ -38,7 +38,7 @@ struct TournamentStatusView: View {
Section {
RowButtonView("Supprimer le tournoi", role: .destructive) {
if tournament.currentPayment == nil {
if tournament.payment == nil {
do {
try dataStore.tournaments.delete(instance: tournament)
} catch {
@ -55,10 +55,10 @@ struct TournamentStatusView: View {
Text("todo: expliquer ce que ca fait")
}
if tournament.hasEnded() == false && tournament.currentCanceled == false {
if tournament.hasEnded() == false && tournament.isCanceled == false {
Section {
RowButtonView("Annuler le tournoi", role: .destructive) {
tournament.setCanceled(true)
tournament.isCanceled = true
dismiss()
}
} footer: {

@ -64,7 +64,7 @@ struct TournamentView: View {
case .canceled:
Section {
RowButtonView("Reprendre le tournoi", role: .destructive) {
tournament.setCanceled(false)
tournament.isCanceled = false
_save()
}
} footer: {

Loading…
Cancel
Save