1/*
2 * username_providers.c: providers for SVN_AUTH_CRED_USERNAME
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24/* ==================================================================== */
25
26
27
28/*** Includes. ***/
29
30#include <apr_pools.h>
31#include "svn_hash.h"
32#include "svn_auth.h"
33#include "svn_error.h"
34#include "svn_utf.h"
35#include "svn_config.h"
36#include "svn_user.h"
37
38
39/*-----------------------------------------------------------------------*/
40/* File provider                                                         */
41/*-----------------------------------------------------------------------*/
42
43/* The key that will be stored on disk.  Serves the same role as similar
44   constants in other providers. */
45#define AUTHN_USERNAME_KEY "username"
46
47
48
49/*** Username-only Provider ***/
50static svn_error_t *
51username_first_creds(void **credentials,
52                     void **iter_baton,
53                     void *provider_baton,
54                     apr_hash_t *parameters,
55                     const char *realmstring,
56                     apr_pool_t *pool)
57{
58  const char *config_dir = svn_hash_gets(parameters,
59                                         SVN_AUTH_PARAM_CONFIG_DIR);
60  const char *username = svn_hash_gets(parameters,
61                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
62  svn_boolean_t may_save = !! username;
63  svn_error_t *err;
64
65  /* If we don't have a usename yet, try the auth cache */
66  if (! username)
67    {
68      apr_hash_t *creds_hash = NULL;
69
70      /* Try to load credentials from a file on disk, based on the
71         realmstring.  Don't throw an error, though: if something went
72         wrong reading the file, no big deal.  What really matters is that
73         we failed to get the creds, so allow the auth system to try the
74         next provider. */
75      err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_USERNAME,
76                                      realmstring, config_dir, pool);
77      svn_error_clear(err);
78      if (! err && creds_hash)
79        {
80          svn_string_t *str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY);
81          if (str && str->data)
82            username = str->data;
83        }
84    }
85
86  /* If that failed, ask the OS for the username */
87  if (! username)
88    username = svn_user_get_name(pool);
89
90  if (username)
91    {
92      svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
93      creds->username = username;
94      creds->may_save = may_save;
95      *credentials = creds;
96    }
97  else
98    *credentials = NULL;
99
100  *iter_baton = NULL;
101
102  return SVN_NO_ERROR;
103}
104
105
106static svn_error_t *
107username_save_creds(svn_boolean_t *saved,
108                    void *credentials,
109                    void *provider_baton,
110                    apr_hash_t *parameters,
111                    const char *realmstring,
112                    apr_pool_t *pool)
113{
114  svn_auth_cred_simple_t *creds = credentials;
115  apr_hash_t *creds_hash = NULL;
116  const char *config_dir;
117  svn_error_t *err;
118
119  *saved = FALSE;
120
121  if (! creds->may_save)
122    return SVN_NO_ERROR;
123
124  config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
125
126  /* Put the credentials in a hash and save it to disk */
127  creds_hash = apr_hash_make(pool);
128  svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY,
129                svn_string_create(creds->username, pool));
130  err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME,
131                                   realmstring, config_dir, pool);
132  svn_error_clear(err);
133  *saved = ! err;
134
135  return SVN_NO_ERROR;
136}
137
138
139static const svn_auth_provider_t username_provider = {
140  SVN_AUTH_CRED_USERNAME,
141  username_first_creds,
142  NULL,
143  username_save_creds
144};
145
146
147/* Public API */
148void
149svn_auth_get_username_provider(svn_auth_provider_object_t **provider,
150                               apr_pool_t *pool)
151{
152  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
153
154  po->vtable = &username_provider;
155  *provider = po;
156}
157
158
159/*-----------------------------------------------------------------------*/
160/* Prompt provider                                                       */
161/*-----------------------------------------------------------------------*/
162
163/* Baton type for username-only prompting. */
164typedef struct username_prompt_provider_baton_t
165{
166  svn_auth_username_prompt_func_t prompt_func;
167  void *prompt_baton;
168
169  /* how many times to re-prompt after the first one fails */
170  int retry_limit;
171} username_prompt_provider_baton_t;
172
173
174/* Iteration baton type for username-only prompting. */
175typedef struct username_prompt_iter_baton_t
176{
177  /* how many times we've reprompted */
178  int retries;
179
180} username_prompt_iter_baton_t;
181
182
183/*** Helper Functions ***/
184static svn_error_t *
185prompt_for_username_creds(svn_auth_cred_username_t **cred_p,
186                          username_prompt_provider_baton_t *pb,
187                          apr_hash_t *parameters,
188                          const char *realmstring,
189                          svn_boolean_t first_time,
190                          svn_boolean_t may_save,
191                          apr_pool_t *pool)
192{
193  const char *def_username = NULL;
194
195  *cred_p = NULL;
196
197  /* If we're allowed to check for default usernames, do so. */
198  if (first_time)
199    def_username = svn_hash_gets(parameters, SVN_AUTH_PARAM_DEFAULT_USERNAME);
200
201  /* If we have defaults, just build the cred here and return it.
202   *
203   * ### I do wonder why this is here instead of in a separate
204   * ### 'defaults' provider that would run before the prompt
205   * ### provider... Hmmm.
206   */
207  if (def_username)
208    {
209      *cred_p = apr_palloc(pool, sizeof(**cred_p));
210      (*cred_p)->username = apr_pstrdup(pool, def_username);
211      (*cred_p)->may_save = TRUE;
212    }
213  else
214    {
215      SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
216                              may_save, pool));
217    }
218
219  return SVN_NO_ERROR;
220}
221
222
223/* Our first attempt will use any default username passed
224   in, and prompt for the remaining stuff. */
225static svn_error_t *
226username_prompt_first_creds(void **credentials_p,
227                            void **iter_baton,
228                            void *provider_baton,
229                            apr_hash_t *parameters,
230                            const char *realmstring,
231                            apr_pool_t *pool)
232{
233  username_prompt_provider_baton_t *pb = provider_baton;
234  username_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
235  const char *no_auth_cache = svn_hash_gets(parameters,
236                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
237
238  SVN_ERR(prompt_for_username_creds
239          ((svn_auth_cred_username_t **) credentials_p, pb,
240           parameters, realmstring, TRUE, ! no_auth_cache, pool));
241
242  ibaton->retries = 0;
243  *iter_baton = ibaton;
244
245  return SVN_NO_ERROR;
246}
247
248
249/* Subsequent attempts to fetch will ignore the default username
250   value, and simply re-prompt for the username, up to a maximum of
251   ib->pb->retry_limit. */
252static svn_error_t *
253username_prompt_next_creds(void **credentials_p,
254                           void *iter_baton,
255                           void *provider_baton,
256                           apr_hash_t *parameters,
257                           const char *realmstring,
258                           apr_pool_t *pool)
259{
260  username_prompt_iter_baton_t *ib = iter_baton;
261  username_prompt_provider_baton_t *pb = provider_baton;
262  const char *no_auth_cache = svn_hash_gets(parameters,
263                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
264
265  if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
266    {
267      /* give up, go on to next provider. */
268      *credentials_p = NULL;
269      return SVN_NO_ERROR;
270    }
271  ib->retries++;
272
273  return prompt_for_username_creds
274         ((svn_auth_cred_username_t **) credentials_p, pb,
275          parameters, realmstring, FALSE, ! no_auth_cache, pool);
276}
277
278
279static const svn_auth_provider_t username_prompt_provider = {
280  SVN_AUTH_CRED_USERNAME,
281  username_prompt_first_creds,
282  username_prompt_next_creds,
283  NULL,
284};
285
286
287/* Public API */
288void
289svn_auth_get_username_prompt_provider
290  (svn_auth_provider_object_t **provider,
291   svn_auth_username_prompt_func_t prompt_func,
292   void *prompt_baton,
293   int retry_limit,
294   apr_pool_t *pool)
295{
296  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
297  username_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
298
299  pb->prompt_func = prompt_func;
300  pb->prompt_baton = prompt_baton;
301  pb->retry_limit = retry_limit;
302
303  po->vtable = &username_prompt_provider;
304  po->provider_baton = pb;
305  *provider = po;
306}
307