Skip to content

Commit dc20ad1

Browse files
committed
limitations on registrations and a single team mode
1 parent fc58864 commit dc20ad1

13 files changed

Lines changed: 444 additions & 69 deletions

Sources/ApiCore/ApiCoreBase.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ public class ApiCoreBase {
7474
} else {
7575
// Create default configuration
7676
_configuration = Configuration(
77+
general: Configuration.General(
78+
singleTeam: false
79+
),
80+
auth: Configuration.Auth(
81+
allowRegistrations: true,
82+
allowInvitations: true,
83+
allowedDomainsForRegistration: []
84+
),
7785
server: Configuration.Server(
7886
name: "API Core!",
7987
url: nil,

Sources/ApiCore/Config/Configuration.swift

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,51 @@ public final class Configuration: Configurable {
1818
case invalidConfigurationData
1919
}
2020

21+
/// General
22+
public final class General: Codable {
23+
24+
/// Single team server (all team functionality will be disabled, all new users will be automatically assigned to the main team if enabled)
25+
/// (disabled by default)
26+
public internal(set) var singleTeam: Bool
27+
28+
enum CodingKeys: String, CodingKey {
29+
case singleTeam = "single_team"
30+
}
31+
32+
/// Initializer
33+
init(singleTeam: Bool) {
34+
self.singleTeam = singleTeam
35+
}
36+
37+
}
38+
39+
/// Authentication
40+
public final class Auth: Codable {
41+
42+
/// Allow new registrations (enabled by default)
43+
public internal(set) var allowRegistrations: Bool
44+
45+
/// Allow new registrations (enabled by default)
46+
public internal(set) var allowInvitations: Bool
47+
48+
/// Domains allowed to go through a self-registration process
49+
public internal(set) var allowedDomainsForRegistration: [String]
50+
51+
enum CodingKeys: String, CodingKey {
52+
case allowRegistrations = "allow_registrations"
53+
case allowInvitations = "allow_invitations"
54+
case allowedDomainsForRegistration = "registration_domains"
55+
}
56+
57+
/// Initializer
58+
init(allowRegistrations: Bool, allowInvitations: Bool, allowedDomainsForRegistration: [String]) {
59+
self.allowRegistrations = allowRegistrations
60+
self.allowInvitations = allowInvitations
61+
self.allowedDomainsForRegistration = allowedDomainsForRegistration
62+
}
63+
64+
}
65+
2166
/// Sertver info
2267
public final class Server: Codable {
2368

@@ -183,6 +228,12 @@ public final class Configuration: Configurable {
183228

184229
}
185230

231+
/// General settings
232+
public internal(set) var general: General
233+
234+
/// Authentication settings
235+
public internal(set) var auth: Auth
236+
186237
/// Server info
187238
public internal(set) var server: Server
188239

@@ -199,6 +250,8 @@ public final class Configuration: Configurable {
199250
public internal(set) var storage: Storage
200251

201252
enum CodingKeys: String, CodingKey {
253+
case general
254+
case auth
202255
case server
203256
case jwtSecret = "jwt_secret"
204257
case database
@@ -207,7 +260,9 @@ public final class Configuration: Configurable {
207260
}
208261

209262
/// Initialization
210-
public init(server: Server, jwtSecret: String, database: Database, mail: Mail, storage: Storage) {
263+
public init(general: General, auth: Auth, server: Server, jwtSecret: String, database: Database, mail: Mail, storage: Storage) {
264+
self.general = general
265+
self.auth = auth
211266
self.server = server
212267
self.jwtSecret = jwtSecret
213268
self.database = database
@@ -224,7 +279,14 @@ extension Configuration {
224279
public func loadEnv() {
225280
// Root
226281
load("apicore.jwt_secret", to: &jwtSecret)
227-
282+
283+
// General
284+
load("apicore.general.single_team", to: &general.singleTeam)
285+
286+
// Auth
287+
load("apicore.auth.allow_registrations", to: &auth.allowRegistrations)
288+
load("apicore.auth.allow_invitations", to: &auth.allowInvitations)
289+
228290
// Mail
229291
load("apicore.mail.email", to: &mail.email)
230292

Sources/ApiCore/Controllers/AuthController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ public class AuthController: Controller {
175175
passwordError = (error as? FrontendError) ?? AuthError.invalidPassword(reason: .generic)
176176
}
177177

178-
// If no error save
178+
// If there is no error, save
179179
if passwordError == nil {
180180
user.password = try password.password.passwordHash(req)
181181
user.verified = true

Sources/ApiCore/Controllers/InstallController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ public class InstallController: Controller {
3939

4040
/// Setup routes
4141
public static func boot(router: Router) throws {
42-
router.get("install") { (req)->Future<Response> in
42+
router.get("install") { req->Future<Response> in
4343
return try install(on: req)
4444
}
4545

46-
router.get("uninstall") { (req)->Future<Response> in
46+
router.get("uninstall") { req->Future<Response> in
4747
return try uninstall(on: req)
4848
}
4949

50-
router.get("reinstall") { (req)->Future<Response> in
50+
router.get("reinstall") { req->Future<Response> in
5151
return try uninstall(on: req).flatMap(to: Response.self) { _ in
5252
return try install(on: req).map(to: Response.self) { _ in
5353
return try req.response.maintenanceFinished(message: "Re-installation finished, login as [email protected]/admin")

Sources/ApiCore/Controllers/LogsController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public class LogsController: Controller {
2121
/// Setup routes
2222
public static func boot(router: Router) throws {
2323
// Print out logged errors
24-
router.get("errors") { (req) -> Future<[ErrorLog]> in
24+
router.get("errors") { req -> Future<[ErrorLog]> in
2525
return ErrorLog.query(on: req).sort(\ErrorLog.added, .descending).all()
2626
}
2727

Sources/ApiCore/Controllers/TeamsController.swift

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ class TeamsController: Controller {
1919
/// Error
2020
enum Error: FrontendError {
2121

22+
/// User has not been found
23+
case singleTeamConfiguration
24+
2225
/// User has not been found
2326
case userNotFound
2427

@@ -41,6 +44,8 @@ class TeamsController: Controller {
4144
/// Erro code
4245
var identifier: String {
4346
switch self {
47+
case .singleTeamConfiguration:
48+
return "team_error.single_team_configuration"
4449
case .userNotFound:
4550
return "team_error.user_not_found"
4651
case .cantAddYourself:
@@ -59,6 +64,8 @@ class TeamsController: Controller {
5964
/// Error reason
6065
var reason: String {
6166
switch self {
67+
case .singleTeamConfiguration:
68+
return "Server is running in a single team configuration"
6269
case .userNotFound:
6370
return "User not found"
6471
case .cantAddYourself:
@@ -93,19 +100,22 @@ class TeamsController: Controller {
93100

94101
/// Setup routes
95102
static func boot(router: Router) throws {
96-
router.get("teams") { (req) -> Future<[Team]> in
103+
router.get("teams") { req -> Future<[Team]> in
97104
let me = try req.me.user()
98105
return try me.teams.query(on: req).paginate(on: req).all().map({ teams in
99106
return teams
100107
})
101108
}
102109

103-
router.get("teams", DbIdentifier.parameter) { (req) -> Future<Team> in
110+
router.get("teams", DbIdentifier.parameter) { req -> Future<Team> in
104111
let id = try req.parameters.next(DbIdentifier.self)
105112
return try req.me.verifiedTeam(id: id)
106113
}
107114

108-
router.post("teams") { (req) -> Future<Response> in
115+
router.post("teams") { req -> Future<Response> in
116+
guard ApiCoreBase.configuration.general.singleTeam == false else {
117+
throw Error.singleTeamConfiguration
118+
}
109119
return try req.content.decode(Team.New.self).flatMap(to: Response.self) { newTeam in
110120
return try Team.exists(identifier: newTeam.identifier, on: req).flatMap(to: Response.self) { identifierExists in
111121
if identifierExists {
@@ -124,7 +134,10 @@ class TeamsController: Controller {
124134
}
125135
}
126136

127-
router.post("teams", "check") { (req) -> Future<Response> in
137+
router.post("teams", "check") { req -> Future<Response> in
138+
guard ApiCoreBase.configuration.general.singleTeam == false else {
139+
throw Error.singleTeamConfiguration
140+
}
128141
return try req.content.decode(Team.Identifier.self).flatMap(to: Response.self) { identifierObject in
129142
return try Team.exists(identifier: identifierObject.identifier, on: req).map(to: Response.self) { identifierExists in
130143
if identifierExists {
@@ -135,7 +148,10 @@ class TeamsController: Controller {
135148
}
136149
}
137150

138-
router.put("teams", DbIdentifier.parameter) { (req) -> Future<Team> in
151+
router.put("teams", DbIdentifier.parameter) { req -> Future<Team> in
152+
guard ApiCoreBase.configuration.general.singleTeam == false else {
153+
throw Error.singleTeamConfiguration
154+
}
139155
let id = try req.parameters.next(DbIdentifier.self)
140156
return try req.me.verifiedTeam(id: id).flatMap(to: Team.self, { team in
141157
return try req.content.decode(Team.New.self).flatMap(to: Team.self) { newTeam in
@@ -166,26 +182,46 @@ class TeamsController: Controller {
166182
}
167183
}
168184

169-
router.get("teams", DbIdentifier.parameter, "users") { (req) -> Future<[User]> in
185+
router.get("teams", DbIdentifier.parameter, "users") { req -> Future<[User]> in
170186
let id = try req.parameters.next(DbIdentifier.self)
171187
return try req.me.verifiedTeam(id: id).flatMap(to: [User].self) { (team) -> Future<[User]> in
172188
return try team.users.query(on: req).paginate(on: req).all()
173189
}
174190
}
175191

176-
router.post("teams", DbIdentifier.parameter, "link") { (req) -> Future<Response> in
192+
router.post("teams", DbIdentifier.parameter, "link") { req -> Future<Response> in
193+
guard ApiCoreBase.configuration.general.singleTeam == false else {
194+
throw Error.singleTeamConfiguration
195+
}
177196
return try processLinking(request: req, action: .link)
178197
}
179198

180-
router.post("teams", DbIdentifier.parameter, "unlink") { (req) -> Future<Response> in
199+
router.post("teams", DbIdentifier.parameter, "unlink") { req -> Future<Response> in
200+
guard ApiCoreBase.configuration.general.singleTeam == false else {
201+
throw Error.singleTeamConfiguration
202+
}
181203
return try processLinking(request: req, action: .unlink)
182204
}
183205

184-
router.delete("teams", DbIdentifier.parameter) { (req) -> Future<Response> in
206+
router.delete("teams", DbIdentifier.parameter) { req -> Future<Response> in
207+
guard ApiCoreBase.configuration.general.singleTeam == false else {
208+
throw Error.singleTeamConfiguration
209+
}
185210
// TODO: Reload JWT token if successful with new info
186211
// QUESTION: Should we make sure user has at least one team?
187212
let teamId = try req.parameters.next(DbIdentifier.self)
188213
return try req.me.verifiedTeam(id: teamId).flatMap(to: Response.self) { (team) -> Future<Response> in
214+
// Common delete function
215+
func delete(team: Team, request req: Request) throws -> Future<Response> {
216+
if team.admin {
217+
throw Error.unableToDeleteAdminTeam
218+
}
219+
// TODO: Cascade through all team data (that is not shared with other teams, possibly delete users too?) !!!!!!
220+
return team.delete(on: req).map(to: Response.self, { (_) -> Response in
221+
return try req.response.deleted()
222+
})
223+
}
224+
189225
if let canDelete = ApiCoreBase.deleteTeamWarning {
190226
return canDelete(team).flatMap(to: Response.self, { (error) -> Future<Response> in
191227
guard let error = error else {
@@ -206,17 +242,6 @@ class TeamsController: Controller {
206242

207243
extension TeamsController {
208244

209-
/// Delete team
210-
private static func delete(team: Team, request req: Request) throws -> Future<Response> {
211-
if team.admin {
212-
throw Error.unableToDeleteAdminTeam
213-
}
214-
// TODO: Cascade through all team data (that is not shared with other teams, possibly delete users too?) !!!!!!
215-
return team.delete(on: req).map(to: Response.self, { (_) -> Response in
216-
return try req.response.deleted()
217-
})
218-
}
219-
220245
/// Process linking from a request
221246
private static func processLinking(request req: Request, action: TeamsController.LinkAction) throws -> Future<Response> {
222247
let teamId = try req.parameters.next(DbIdentifier.self)

0 commit comments

Comments
 (0)