public long GetUserSyncToken(User user) { using (SQLiteConnection conn = new SQLiteConnection(String.Format("Data Source={0};Version=3;", "DB/management.db3"))) { conn.Open(); using (SQLiteCommand cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT synctoken FROM users_updates WHERE iduser=$iduser"; cmd.Parameters.AddWithValue("iduser",user.Id); long stoken = (long)cmd.ExecuteScalar(); return stoken; } } }
// Da usare solo lato server non esporla al protocollo di rete. // TODO cache memoria public User LoginT(string username, string accessToken) { try { // Check iniziali su lunghezze minime e null checks // TODO // TODO: migliorare con cache in memoria degli utenti loggati. long id; // ------------------ NOTA SUL LOCK ------------------ // In questo caso non tocchiamo il DB in scrittura, ma solo in lettura. Sono possibili corse critiche con letture spurie. // In realtà per noi non è un problema, perchè al prossimo tentativo di SYNC verrà richiesto il token all'utente e ne verrà // controllata la validità. // Eventuali eccezioni qui SQLiteConnection conn; using (conn = new SQLiteConnection("Data Source=DB/management.db3;Version=3;")) { conn.Open(); // Seleziona da database tramite username e token using (SQLiteCommand cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT id, username, accesstoken, expire FROM users WHERE username=$username and accesstoken=$accesstoken"; cmd.Parameters.AddWithValue("username", username); cmd.Parameters.AddWithValue("accesstoken", accessToken); using (var r = cmd.ExecuteReader()) { // Se non ho risultati, il login è errato if (!r.Read()) throw new UserLoginException("Token non valido o non legato all'utente specificato."); id = r.GetInt64(0); // Se l'access token è nullo, il login è invalido. if (r.IsDBNull(2)) throw new UserLoginException("Token non valido o non legato all'utente specificato."); else { // Controllo se il token è scaduto. if (r.IsDBNull(3) || DateTime.Parse(r.GetString(3)) < DateTime.Now) throw new UserLoginException("Token scaduto. Si prega di rieffettuare il login"); } } // Se esiste già un'altra connessione relativa allo stesso utente, ritorna il medesimo oggetto User // in modo da garantire sincronia. Nota: solo questo oggetto può scrivere su _connectedUsers, quindi, avendo già il lock qui, // non devo riacquisirlo. In altre parole posso manipolare in lettura e scrittura la mappa solo da questo metodo. // Ergo sono già sincronizzato. if (!_connctedUsers.ContainsKey(id)) { User u = new User(id, username); _connctedUsers.Add(id, u); } // Incrementa il reference counting per l'oggetto che ritorniamo User logged = _connctedUsers[id]; //Il reference counting non vale per gli utenti che usano il token //logged.IncConnections(); return logged; } } } catch (Exception e) { throw new UserLoginException(e.Message); } }
public void Logout(User user) { try { // ------------------ NOTA SUL LOCK ------------------ // In questo caso non tocchiamo il DB in scrittura, ma solo in lettura. Sono possibili corse critiche con letture spurie. // In realtà per noi non è un problema, perchè al prossimo tentativo di SYNC verrà richiesto il token all'utente e ne verrà // controllata la validità. // Eventuali eccezioni qui SQLiteConnection conn; using (conn = new SQLiteConnection("Data Source=DB/management.db3;Version=3;")) { conn.Open(); // Seleziona da database tramite username e token using (SQLiteCommand cmd = conn.CreateCommand()) { cmd.CommandText = "UPDATE users SET accesstoken=NULL,expire=DATE() where id=$id"; cmd.Parameters.AddWithValue("id", user.Id); if (cmd.ExecuteNonQuery() != 1) throw new UserLogoutException("Non è stata aggiornata alcuna riga sul db. L'utente potrebbe essere invalido o già sloggato."); // Se l'utente che sta effettuando logout è nella lista dello user manager, eliminiamo la entry corrispondente. // in questo modo tutte le altre connessioni avranno la cache invalidata. if (_connctedUsers.ContainsKey(user.Id)) { _connctedUsers.Remove(user.Id); } } } } catch (Exception e) { throw new UserLoginException(e.Message); } }
public void RemoveConnectedUser(User user) { lock (this) { if (_connctedUsers.ContainsKey(user.Id)) _connctedUsers.Remove(user.Id); } }
public User LoginUP(string username,string password, out string accesstoken, out DateTime expirationdate) { try { // Check iniziali su lunghezze minime e null checks //TODO long id; // ******************* user manager è l'unico entrypoint per modificate la tabella users *********** lock (this) { SQLiteConnection conn; using (conn = new SQLiteConnection("Data Source=DB/management.db3;Version=3;")) { conn.Open(); // Abbiamo bisogno di una transazione perchè dobbiamo aggiornare l'ultima data di login solo se questo va a buon fine. using (SQLiteTransaction tr = conn.BeginTransaction()) { try { // Seleziona da database l'utente in base a username e password using (SQLiteCommand cmd = conn.CreateCommand()) { // Hasha la password string encpwd = Hash(password); cmd.CommandText = "SELECT id, username, accesstoken, expire FROM users WHERE username=$username and password=$password"; cmd.Parameters.AddWithValue("username", username); cmd.Parameters.AddWithValue("password", encpwd); using (var r = cmd.ExecuteReader()) { // Se non ho risultati, il login è errato if (!r.Read()) throw new UserLoginException("Combinazione username/password non valida."); id = r.GetInt64(0); // at può essere nullo nel caso in cui questo sia il primo login. In tal caso, devo generare un token if (r.IsDBNull(2)) { accesstoken = Guid.NewGuid().ToString(); } else { // Se il token esiste, controlla che non sia scaduto. if (r.IsDBNull(3) || DateTime.Parse(r.GetString(3))<DateTime.Now) accesstoken = Guid.NewGuid().ToString(); accesstoken = r.GetString(2); } } // A questo punto prolunghiamo la validit dell'access token expirationdate = DateTime.Now; expirationdate = expirationdate.Add(TOKEN_VALIDITY_SPAN); // Aggiorno i dati su db cmd.Parameters.Clear(); cmd.CommandText = "UPDATE users SET accesstoken=$accesstoken, lastlogin=DATE(), expire=$expire WHERE id=$id"; cmd.Parameters.AddWithValue("accesstoken", accesstoken); cmd.Parameters.AddWithValue("expire", expirationdate.ToString("yyyy-MM-dd HH:mm:ss")); //todo check this cmd.Parameters.AddWithValue("id", id); int res = cmd.ExecuteNonQuery(); if (res != 1) throw new UserLoginException("Errore durante l'aggiornamento dati nel database."); // Se tutto è andato bene, effettua il commit. tr.Commit(); // Se esiste già un'altra connessione relativa allo stesso utente, ritorna il medesimo oggetto User // in modo da garantire sincronia. Nota: solo questo oggetto può scrivere su _connectedUsers, quindi, avendo già il lock qui, // non devo riacquisirlo. In altre parole posso manipolare in lettura e scrittura la mappa solo da questo metodo. // Ergo sono già sincronizzato. if (!_connctedUsers.ContainsKey(id)) { User u = new User(id, username); _connctedUsers.Add(id,u); } // Incrementa il reference counting per l'oggetto che ritorniamo User logged = _connctedUsers[id]; //Il reference counting non vale per gli utenti che usano il token //logged.IncConnections(); return logged; } } catch (Exception e) { // Errore generico: rollback tr.Rollback(); // E lascialo gestire allo scope più alto. throw e; } } } } } catch (Exception e) { throw new UserLoginException(e.Message); } }