AuthenticationRepository.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Emby.Server.Implementations.Data;
  9. using MediaBrowser.Controller;
  10. using MediaBrowser.Controller.Security;
  11. using MediaBrowser.Model.Logging;
  12. using MediaBrowser.Model.Querying;
  13. using SQLitePCL.pretty;
  14. namespace Emby.Server.Implementations.Security
  15. {
  16. public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository
  17. {
  18. private readonly IServerApplicationPaths _appPaths;
  19. private readonly CultureInfo _usCulture = new CultureInfo("en-US");
  20. public AuthenticationRepository(ILogger logger, IServerApplicationPaths appPaths)
  21. : base(logger)
  22. {
  23. _appPaths = appPaths;
  24. DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db");
  25. }
  26. public void Initialize()
  27. {
  28. using (var connection = CreateConnection())
  29. {
  30. connection.ExecuteAll(string.Join(";", new[]
  31. {
  32. "PRAGMA page_size=4096",
  33. "pragma default_temp_store = memory",
  34. "pragma temp_store = memory"
  35. }));
  36. string[] queries = {
  37. "create table if not exists AccessTokens (Id GUID PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT, AppName TEXT, AppVersion TEXT, DeviceName TEXT, UserId TEXT, IsActive BIT, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)",
  38. "create index if not exists idx_AccessTokens on AccessTokens(Id)"
  39. };
  40. connection.RunQueries(queries);
  41. connection.RunInTransaction(db =>
  42. {
  43. var existingColumnNames = GetColumnNames(db, "AccessTokens");
  44. AddColumn(db, "AccessTokens", "AppVersion", "TEXT", existingColumnNames);
  45. }, TransactionMode);
  46. }
  47. }
  48. public Task Create(AuthenticationInfo info, CancellationToken cancellationToken)
  49. {
  50. info.Id = Guid.NewGuid().ToString("N");
  51. return Update(info, cancellationToken);
  52. }
  53. public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken)
  54. {
  55. if (info == null)
  56. {
  57. throw new ArgumentNullException("info");
  58. }
  59. cancellationToken.ThrowIfCancellationRequested();
  60. using (var connection = CreateConnection())
  61. {
  62. using (WriteLock.Write())
  63. {
  64. connection.RunInTransaction(db =>
  65. {
  66. using (var statement = db.PrepareStatement("replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)"))
  67. {
  68. statement.TryBind("@Id", info.Id.ToGuidParamValue());
  69. statement.TryBind("@AccessToken", info.AccessToken);
  70. statement.TryBind("@DeviceId", info.DeviceId);
  71. statement.TryBind("@AppName", info.AppName);
  72. statement.TryBind("@AppVersion", info.AppVersion);
  73. statement.TryBind("@DeviceName", info.DeviceName);
  74. statement.TryBind("@UserId", info.UserId);
  75. statement.TryBind("@IsActive", info.IsActive);
  76. statement.TryBind("@DateCreated", info.DateCreated.ToDateTimeParamValue());
  77. if (info.DateRevoked.HasValue)
  78. {
  79. statement.TryBind("@DateRevoked", info.DateRevoked.Value.ToDateTimeParamValue());
  80. }
  81. else
  82. {
  83. statement.TryBindNull("@DateRevoked");
  84. }
  85. statement.MoveNext();
  86. }
  87. }, TransactionMode);
  88. }
  89. }
  90. }
  91. private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens";
  92. private void BindAuthenticationQueryParams(AuthenticationInfoQuery query, IStatement statement)
  93. {
  94. if (!string.IsNullOrWhiteSpace(query.AccessToken))
  95. {
  96. statement.TryBind("@AccessToken", query.AccessToken);
  97. }
  98. if (!string.IsNullOrWhiteSpace(query.UserId))
  99. {
  100. statement.TryBind("@UserId", query.UserId);
  101. }
  102. if (!string.IsNullOrWhiteSpace(query.DeviceId))
  103. {
  104. statement.TryBind("@DeviceId", query.DeviceId);
  105. }
  106. if (query.IsActive.HasValue)
  107. {
  108. statement.TryBind("@IsActive", query.IsActive.Value);
  109. }
  110. }
  111. public QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query)
  112. {
  113. if (query == null)
  114. {
  115. throw new ArgumentNullException("query");
  116. }
  117. using (var connection = CreateConnection(true))
  118. {
  119. using (WriteLock.Read())
  120. {
  121. var commandText = BaseSelectText;
  122. var whereClauses = new List<string>();
  123. var startIndex = query.StartIndex ?? 0;
  124. if (!string.IsNullOrWhiteSpace(query.AccessToken))
  125. {
  126. whereClauses.Add("AccessToken=@AccessToken");
  127. }
  128. if (!string.IsNullOrWhiteSpace(query.UserId))
  129. {
  130. whereClauses.Add("UserId=@UserId");
  131. }
  132. if (!string.IsNullOrWhiteSpace(query.DeviceId))
  133. {
  134. whereClauses.Add("DeviceId=@DeviceId");
  135. }
  136. if (query.IsActive.HasValue)
  137. {
  138. whereClauses.Add("IsActive=@IsActive");
  139. }
  140. if (query.HasUser.HasValue)
  141. {
  142. if (query.HasUser.Value)
  143. {
  144. whereClauses.Add("UserId not null");
  145. }
  146. else
  147. {
  148. whereClauses.Add("UserId is null");
  149. }
  150. }
  151. var whereTextWithoutPaging = whereClauses.Count == 0 ?
  152. string.Empty :
  153. " where " + string.Join(" AND ", whereClauses.ToArray());
  154. if (startIndex > 0)
  155. {
  156. var pagingWhereText = whereClauses.Count == 0 ?
  157. string.Empty :
  158. " where " + string.Join(" AND ", whereClauses.ToArray());
  159. whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})",
  160. pagingWhereText,
  161. startIndex.ToString(_usCulture)));
  162. }
  163. var whereText = whereClauses.Count == 0 ?
  164. string.Empty :
  165. " where " + string.Join(" AND ", whereClauses.ToArray());
  166. commandText += whereText;
  167. commandText += " ORDER BY DateCreated";
  168. if (query.Limit.HasValue)
  169. {
  170. commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture);
  171. }
  172. var list = new List<AuthenticationInfo>();
  173. using (var statement = connection.PrepareStatement(commandText))
  174. {
  175. BindAuthenticationQueryParams(query, statement);
  176. foreach (var row in statement.ExecuteQuery())
  177. {
  178. list.Add(Get(row));
  179. }
  180. using (var totalCountStatement = connection.PrepareStatement("select count (Id) from AccessTokens" + whereTextWithoutPaging))
  181. {
  182. BindAuthenticationQueryParams(query, totalCountStatement);
  183. var count = totalCountStatement.ExecuteQuery()
  184. .SelectScalarInt()
  185. .First();
  186. return new QueryResult<AuthenticationInfo>()
  187. {
  188. Items = list.ToArray(),
  189. TotalRecordCount = count
  190. };
  191. }
  192. }
  193. }
  194. }
  195. }
  196. public AuthenticationInfo Get(string id)
  197. {
  198. if (string.IsNullOrEmpty(id))
  199. {
  200. throw new ArgumentNullException("id");
  201. }
  202. using (var connection = CreateConnection(true))
  203. {
  204. using (WriteLock.Read())
  205. {
  206. var commandText = BaseSelectText + " where Id=@Id";
  207. using (var statement = connection.PrepareStatement(commandText))
  208. {
  209. statement.BindParameters["@Id"].Bind(id.ToGuidParamValue());
  210. foreach (var row in statement.ExecuteQuery())
  211. {
  212. return Get(row);
  213. }
  214. return null;
  215. }
  216. }
  217. }
  218. }
  219. private AuthenticationInfo Get(IReadOnlyList<IResultSetValue> reader)
  220. {
  221. var info = new AuthenticationInfo
  222. {
  223. Id = reader[0].ReadGuid().ToString("N"),
  224. AccessToken = reader[1].ToString()
  225. };
  226. if (reader[2].SQLiteType != SQLiteType.Null)
  227. {
  228. info.DeviceId = reader[2].ToString();
  229. }
  230. if (reader[3].SQLiteType != SQLiteType.Null)
  231. {
  232. info.AppName = reader[3].ToString();
  233. }
  234. if (reader[4].SQLiteType != SQLiteType.Null)
  235. {
  236. info.AppVersion = reader[4].ToString();
  237. }
  238. if (reader[5].SQLiteType != SQLiteType.Null)
  239. {
  240. info.DeviceName = reader[5].ToString();
  241. }
  242. if (reader[6].SQLiteType != SQLiteType.Null)
  243. {
  244. info.UserId = reader[6].ToString();
  245. }
  246. info.IsActive = reader[7].ToBool();
  247. info.DateCreated = reader[8].ReadDateTime();
  248. if (reader[9].SQLiteType != SQLiteType.Null)
  249. {
  250. info.DateRevoked = reader[9].ReadDateTime();
  251. }
  252. return info;
  253. }
  254. }
  255. }