Passed
Pull Request — master (#182)
by Vincent
13:09 queued 02:56
created

isAuthenticated(AuthenticationAccount)   A

Complexity

Conditions 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
dl 0
loc 6
ccs 3
cts 3
cp 1
crap 2
rs 10
c 1
b 0
f 0
1
/*
2
 * This file is part of Araknemu.
3
 *
4
 * Araknemu is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * Araknemu is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with Araknemu.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.quatrevieux.araknemu.realm.authentication;
21
22
import fr.quatrevieux.araknemu.common.account.banishment.BanishmentService;
23
import fr.quatrevieux.araknemu.core.dbal.repository.EntityNotFoundException;
24
import fr.quatrevieux.araknemu.core.event.EventsSubscriber;
25
import fr.quatrevieux.araknemu.core.event.Listener;
26
import fr.quatrevieux.araknemu.data.living.entity.account.Account;
27
import fr.quatrevieux.araknemu.data.living.repository.account.AccountRepository;
28
import fr.quatrevieux.araknemu.realm.authentication.password.PasswordManager;
29
import fr.quatrevieux.araknemu.realm.host.HostService;
30
import fr.quatrevieux.araknemu.realm.listener.SendUpdatedHostList;
31
32
import java.util.Collection;
33
import java.util.Collections;
34
import java.util.HashSet;
35
import java.util.Set;
36
import java.util.concurrent.ConcurrentHashMap;
37
import java.util.concurrent.ConcurrentMap;
38
39
/**
40
 * Service for handle authentication
41
 */
42
public final class AuthenticationService implements EventsSubscriber {
43
    private final AccountRepository repository;
44
    private final HostService hosts;
45
    private final PasswordManager passwordManager;
46
    private final BanishmentService<AuthenticationAccount> banishmentService;
47
48
    /**
49
     * Set of accounts which wait for authentication process
50
     * There are loaded, but not yet authenticated
51
     */
52 1
    private final Set<AuthenticationAccount> pending = Collections.synchronizedSet(new HashSet<>());
53
54
    /**
55
     * Map of authenticated accounts
56
     * There are linked to a session
57
     */
58 1
    private final ConcurrentMap<Integer, AuthenticationAccount> authenticated = new ConcurrentHashMap<>();
59
60 1
    public AuthenticationService(AccountRepository repository, HostService hosts, PasswordManager passwordManager, BanishmentService<AuthenticationAccount> banishmentService) {
61 1
        this.repository = repository;
62 1
        this.hosts = hosts;
63 1
        this.passwordManager = passwordManager;
64 1
        this.banishmentService = banishmentService;
65 1
    }
66
67
    /**
68
     * Perform authenticate request
69
     *
70
     * This method is synchronized to ensure that two account are not requested login
71
     * in the same time
72
     */
73
    public synchronized void authenticate(AuthenticationRequest request) {
74
        final AuthenticationAccount account;
75
76
        try {
77 1
            account = getAccount(request.username());
78 1
        } catch (EntityNotFoundException e) {
79 1
            request.invalidCredentials();
80 1
            return;
81 1
        }
82
83 1
        if (banishmentService.isBanned(account)) {
84 1
            request.banned();
85 1
            return;
86
        }
87
88 1
        if (!account.password().check(request.password())) {
89 1
            request.invalidCredentials();
90 1
            return;
91
        }
92
93 1
        passwordManager.rehash(account.password(), request.password(), account::updatePassword);
94
95 1
        if (
96 1
            isAuthenticated(account)
97 1
            || pending.contains(account)
98
        ) {
99 1
            request.alreadyConnected();
100 1
            return;
101
        }
102
103 1
        pending.add(account);
104
105 1
        hosts.checkLogin(account, response -> {
106 1
            if (response) {
107 1
                request.isPlaying();
108
            } else {
109 1
                request.success(account);
110
            }
111
112 1
            pending.remove(account);
113 1
        });
114 1
    }
115
116
    /**
117
     * Check if the account is authenticated
118
     */
119
    public boolean isAuthenticated(AuthenticationAccount account) {
120 1
        if (!authenticated.containsKey(account.id())) {
121 1
            return false;
122
        }
123
124 1
        return authenticated.get(account.id()).isLogged();
125
    }
126
127
    /**
128
     * Get list of all authenticated accounts
129
     */
130
    public Collection<AuthenticationAccount> authenticatedAccounts() {
131 1
        return authenticated.values();
132
    }
133
134
    @Override
135
    public Listener[] listeners() {
136 1
        return new Listener[] {
137
            new SendUpdatedHostList(this),
138
        };
139
    }
140
141
    /**
142
     * Remove account from authenticated accounts
143
     */
144
    void logout(AuthenticationAccount account) {
145 1
        authenticated.remove(account.id());
146 1
    }
147
148
    /**
149
     * Add account to authenticated account
150
     */
151
    void login(AuthenticationAccount account) {
152 1
        authenticated.put(account.id(), account);
153 1
    }
154
155
    void savePassword(Account account) {
156 1
        repository.savePassword(account);
157 1
    }
158
159
    private AuthenticationAccount getAccount(String username) {
160 1
        final Account account = repository.findByUsername(username);
161
162 1
        return new AuthenticationAccount(account, passwordManager.get(account.password()), this);
163
    }
164
}
165