Merge "Replace "Bug37714" by "T39714""
[lhc/web/wiklou.git] / resources / src / mediawiki.user.js
1 /**
2 * @class mw.user
3 * @singleton
4 */
5 /* global Uint16Array */
6 ( function ( mw, $ ) {
7 var userInfoPromise, pageviewRandomId;
8
9 /**
10 * Get the current user's groups or rights
11 *
12 * @private
13 * @return {jQuery.Promise}
14 */
15 function getUserInfo() {
16 if ( !userInfoPromise ) {
17 userInfoPromise = new mw.Api().getUserInfo();
18 }
19 return userInfoPromise;
20 }
21
22 // mw.user with the properties options and tokens gets defined in mediawiki.js.
23 $.extend( mw.user, {
24
25 /**
26 * Generate a random user session ID.
27 *
28 * This information would potentially be stored in a cookie to identify a user during a
29 * session or series of sessions. Its uniqueness should not be depended on unless the
30 * browser supports the crypto API.
31 *
32 * Known problems with Math.random():
33 * Using the Math.random function we have seen sets
34 * with 1% of non uniques among 200,000 values with Safari providing most of these.
35 * Given the prevalence of Safari in mobile the percentage of duplicates in
36 * mobile usages of this code is probably higher.
37 *
38 * Rationale:
39 * We need about 80 bits to make sure that probability of collision
40 * on 155 billion is <= 1%
41 *
42 * See https://en.wikipedia.org/wiki/Birthday_attack#Mathematics
43 * n(p;H) = n(0.01,2^80)= sqrt (2 * 2^80 * ln(1/(1-0.01)))
44
45 * @return {string} 80 bit integer in hex format, padded
46 */
47 generateRandomSessionId: function () {
48 var rnds, i,
49 // Support: IE 11
50 crypto = window.crypto || window.msCrypto;
51
52 if ( crypto && crypto.getRandomValues && typeof Uint16Array === 'function' ) {
53 // Fill an array with 5 random values, each of which is 16 bits.
54 // Note that Uint16Array is array-like but does not implement Array.
55 rnds = new Uint16Array( 5 );
56 crypto.getRandomValues( rnds );
57 } else {
58 // 0x10000 is 2^16 so the operation below will return a number
59 // between 2^16 and zero
60 for ( i = 0; i < 5; i++ ) {
61 rnds[ i ] = Math.floor( Math.random() * 0x10000 );
62 }
63 }
64
65 // Convert the 5 16bit-numbers into 20 characters (4 hex per 16 bits).
66 // Concatenation of two random integers with entropy n and m
67 // returns a string with entropy n+m if those strings are independent.
68 // Tested that below code is faster than array + loop + join.
69 return ( rnds[ 0 ] + 0x10000 ).toString( 16 ).slice( 1 ) +
70 ( rnds[ 1 ] + 0x10000 ).toString( 16 ).slice( 1 ) +
71 ( rnds[ 2 ] + 0x10000 ).toString( 16 ).slice( 1 ) +
72 ( rnds[ 3 ] + 0x10000 ).toString( 16 ).slice( 1 ) +
73 ( rnds[ 4 ] + 0x10000 ).toString( 16 ).slice( 1 );
74 },
75
76 /**
77 * A sticky generateRandomSessionId for the current JS execution context,
78 * cached within this class (also known as a page view token).
79 *
80 * @since 1.32
81 * @return {string} 64 bit integer in hex format, padded
82 */
83 getPageviewToken: function () {
84 if ( !pageviewRandomId ) {
85 pageviewRandomId = mw.user.generateRandomSessionId();
86 }
87
88 return pageviewRandomId;
89 },
90
91 /**
92 * Get the current user's database id
93 *
94 * Not to be confused with #id.
95 *
96 * @return {number} Current user's id, or 0 if user is anonymous
97 */
98 getId: function () {
99 return mw.config.get( 'wgUserId' ) || 0;
100 },
101
102 /**
103 * Get the current user's name
104 *
105 * @return {string|null} User name string or null if user is anonymous
106 */
107 getName: function () {
108 return mw.config.get( 'wgUserName' );
109 },
110
111 /**
112 * Get date user registered, if available
113 *
114 * @return {boolean|null|Date} False for anonymous users, null if data is
115 * unavailable, or Date for when the user registered.
116 */
117 getRegistration: function () {
118 var registration;
119 if ( mw.user.isAnon() ) {
120 return false;
121 }
122 registration = mw.config.get( 'wgUserRegistration' );
123 // Registration may be unavailable if the user signed up before MediaWiki
124 // began tracking this.
125 return !registration ? null : new Date( registration );
126 },
127
128 /**
129 * Whether the current user is anonymous
130 *
131 * @return {boolean}
132 */
133 isAnon: function () {
134 return mw.user.getName() === null;
135 },
136
137 /**
138 * Get an automatically generated random ID (persisted in sessionStorage)
139 *
140 * This ID is ephemeral for everyone, staying in their browser only until they
141 * close their browsing session.
142 *
143 * @return {string} Random session ID
144 */
145 sessionId: function () {
146 var sessionId = mw.storage.session.get( 'mwuser-sessionId' );
147 if ( !sessionId ) {
148 sessionId = mw.user.generateRandomSessionId();
149 mw.storage.session.set( 'mwuser-sessionId', sessionId );
150 }
151 return sessionId;
152 },
153
154 /**
155 * Get the current user's name or the session ID
156 *
157 * Not to be confused with #getId.
158 *
159 * @return {string} User name or random session ID
160 */
161 id: function () {
162 return mw.user.getName() || mw.user.sessionId();
163 },
164
165 /**
166 * Get the current user's groups
167 *
168 * @param {Function} [callback]
169 * @return {jQuery.Promise}
170 */
171 getGroups: function ( callback ) {
172 var userGroups = mw.config.get( 'wgUserGroups', [] );
173
174 // Uses promise for backwards compatibility
175 return $.Deferred().resolve( userGroups ).done( callback );
176 },
177
178 /**
179 * Get the current user's rights
180 *
181 * @param {Function} [callback]
182 * @return {jQuery.Promise}
183 */
184 getRights: function ( callback ) {
185 return getUserInfo().then(
186 function ( userInfo ) { return userInfo.rights; },
187 function () { return []; }
188 ).done( callback );
189 }
190 } );
191
192 /**
193 * @method stickyRandomId
194 * @deprecated since 1.32 use getPageviewToken instead
195 */
196 mw.log.deprecate( mw.user, 'stickyRandomId', mw.user.getPageviewToken, 'Please use getPageviewToken instead' );
197
198 }( mediaWiki, jQuery ) );