1251881Speter/*
2251881Speter * username_providers.c: providers for SVN_AUTH_CRED_USERNAME
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter
30251881Speter#include <apr_pools.h>
31251881Speter#include "svn_hash.h"
32251881Speter#include "svn_auth.h"
33251881Speter#include "svn_error.h"
34251881Speter#include "svn_utf.h"
35251881Speter#include "svn_config.h"
36251881Speter#include "svn_user.h"
37251881Speter
38251881Speter
39251881Speter/*-----------------------------------------------------------------------*/
40251881Speter/* File provider                                                         */
41251881Speter/*-----------------------------------------------------------------------*/
42251881Speter
43251881Speter/* The key that will be stored on disk.  Serves the same role as similar
44251881Speter   constants in other providers. */
45251881Speter#define AUTHN_USERNAME_KEY "username"
46251881Speter
47251881Speter
48251881Speter
49251881Speter/*** Username-only Provider ***/
50251881Speterstatic svn_error_t *
51251881Speterusername_first_creds(void **credentials,
52251881Speter                     void **iter_baton,
53251881Speter                     void *provider_baton,
54251881Speter                     apr_hash_t *parameters,
55251881Speter                     const char *realmstring,
56251881Speter                     apr_pool_t *pool)
57251881Speter{
58251881Speter  const char *config_dir = svn_hash_gets(parameters,
59251881Speter                                         SVN_AUTH_PARAM_CONFIG_DIR);
60251881Speter  const char *username = svn_hash_gets(parameters,
61251881Speter                                       SVN_AUTH_PARAM_DEFAULT_USERNAME);
62251881Speter  svn_boolean_t may_save = !! username;
63251881Speter  svn_error_t *err;
64251881Speter
65251881Speter  /* If we don't have a usename yet, try the auth cache */
66251881Speter  if (! username)
67251881Speter    {
68251881Speter      apr_hash_t *creds_hash = NULL;
69251881Speter
70251881Speter      /* Try to load credentials from a file on disk, based on the
71251881Speter         realmstring.  Don't throw an error, though: if something went
72251881Speter         wrong reading the file, no big deal.  What really matters is that
73251881Speter         we failed to get the creds, so allow the auth system to try the
74251881Speter         next provider. */
75251881Speter      err = svn_config_read_auth_data(&creds_hash, SVN_AUTH_CRED_USERNAME,
76251881Speter                                      realmstring, config_dir, pool);
77251881Speter      svn_error_clear(err);
78251881Speter      if (! err && creds_hash)
79251881Speter        {
80251881Speter          svn_string_t *str = svn_hash_gets(creds_hash, AUTHN_USERNAME_KEY);
81251881Speter          if (str && str->data)
82251881Speter            username = str->data;
83251881Speter        }
84251881Speter    }
85251881Speter
86251881Speter  /* If that failed, ask the OS for the username */
87251881Speter  if (! username)
88251881Speter    username = svn_user_get_name(pool);
89251881Speter
90251881Speter  if (username)
91251881Speter    {
92251881Speter      svn_auth_cred_simple_t *creds = apr_pcalloc(pool, sizeof(*creds));
93251881Speter      creds->username = username;
94251881Speter      creds->may_save = may_save;
95251881Speter      *credentials = creds;
96251881Speter    }
97251881Speter  else
98251881Speter    *credentials = NULL;
99251881Speter
100251881Speter  *iter_baton = NULL;
101251881Speter
102251881Speter  return SVN_NO_ERROR;
103251881Speter}
104251881Speter
105251881Speter
106251881Speterstatic svn_error_t *
107251881Speterusername_save_creds(svn_boolean_t *saved,
108251881Speter                    void *credentials,
109251881Speter                    void *provider_baton,
110251881Speter                    apr_hash_t *parameters,
111251881Speter                    const char *realmstring,
112251881Speter                    apr_pool_t *pool)
113251881Speter{
114251881Speter  svn_auth_cred_simple_t *creds = credentials;
115251881Speter  apr_hash_t *creds_hash = NULL;
116251881Speter  const char *config_dir;
117251881Speter  svn_error_t *err;
118251881Speter
119251881Speter  *saved = FALSE;
120251881Speter
121251881Speter  if (! creds->may_save)
122251881Speter    return SVN_NO_ERROR;
123251881Speter
124251881Speter  config_dir = svn_hash_gets(parameters, SVN_AUTH_PARAM_CONFIG_DIR);
125251881Speter
126251881Speter  /* Put the credentials in a hash and save it to disk */
127251881Speter  creds_hash = apr_hash_make(pool);
128251881Speter  svn_hash_sets(creds_hash, AUTHN_USERNAME_KEY,
129251881Speter                svn_string_create(creds->username, pool));
130251881Speter  err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_USERNAME,
131251881Speter                                   realmstring, config_dir, pool);
132251881Speter  svn_error_clear(err);
133251881Speter  *saved = ! err;
134251881Speter
135251881Speter  return SVN_NO_ERROR;
136251881Speter}
137251881Speter
138251881Speter
139251881Speterstatic const svn_auth_provider_t username_provider = {
140251881Speter  SVN_AUTH_CRED_USERNAME,
141251881Speter  username_first_creds,
142251881Speter  NULL,
143251881Speter  username_save_creds
144251881Speter};
145251881Speter
146251881Speter
147251881Speter/* Public API */
148251881Spetervoid
149251881Spetersvn_auth_get_username_provider(svn_auth_provider_object_t **provider,
150251881Speter                               apr_pool_t *pool)
151251881Speter{
152251881Speter  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
153251881Speter
154251881Speter  po->vtable = &username_provider;
155251881Speter  *provider = po;
156251881Speter}
157251881Speter
158251881Speter
159251881Speter/*-----------------------------------------------------------------------*/
160251881Speter/* Prompt provider                                                       */
161251881Speter/*-----------------------------------------------------------------------*/
162251881Speter
163251881Speter/* Baton type for username-only prompting. */
164251881Spetertypedef struct username_prompt_provider_baton_t
165251881Speter{
166251881Speter  svn_auth_username_prompt_func_t prompt_func;
167251881Speter  void *prompt_baton;
168251881Speter
169251881Speter  /* how many times to re-prompt after the first one fails */
170251881Speter  int retry_limit;
171251881Speter} username_prompt_provider_baton_t;
172251881Speter
173251881Speter
174251881Speter/* Iteration baton type for username-only prompting. */
175251881Spetertypedef struct username_prompt_iter_baton_t
176251881Speter{
177251881Speter  /* how many times we've reprompted */
178251881Speter  int retries;
179251881Speter
180251881Speter} username_prompt_iter_baton_t;
181251881Speter
182251881Speter
183251881Speter/*** Helper Functions ***/
184251881Speterstatic svn_error_t *
185251881Speterprompt_for_username_creds(svn_auth_cred_username_t **cred_p,
186251881Speter                          username_prompt_provider_baton_t *pb,
187251881Speter                          apr_hash_t *parameters,
188251881Speter                          const char *realmstring,
189251881Speter                          svn_boolean_t first_time,
190251881Speter                          svn_boolean_t may_save,
191251881Speter                          apr_pool_t *pool)
192251881Speter{
193251881Speter  const char *def_username = NULL;
194251881Speter
195251881Speter  *cred_p = NULL;
196251881Speter
197251881Speter  /* If we're allowed to check for default usernames, do so. */
198251881Speter  if (first_time)
199251881Speter    def_username = svn_hash_gets(parameters, SVN_AUTH_PARAM_DEFAULT_USERNAME);
200251881Speter
201251881Speter  /* If we have defaults, just build the cred here and return it.
202251881Speter   *
203251881Speter   * ### I do wonder why this is here instead of in a separate
204251881Speter   * ### 'defaults' provider that would run before the prompt
205251881Speter   * ### provider... Hmmm.
206251881Speter   */
207251881Speter  if (def_username)
208251881Speter    {
209251881Speter      *cred_p = apr_palloc(pool, sizeof(**cred_p));
210251881Speter      (*cred_p)->username = apr_pstrdup(pool, def_username);
211251881Speter      (*cred_p)->may_save = TRUE;
212251881Speter    }
213251881Speter  else
214251881Speter    {
215251881Speter      SVN_ERR(pb->prompt_func(cred_p, pb->prompt_baton, realmstring,
216251881Speter                              may_save, pool));
217251881Speter    }
218251881Speter
219251881Speter  return SVN_NO_ERROR;
220251881Speter}
221251881Speter
222251881Speter
223251881Speter/* Our first attempt will use any default username passed
224251881Speter   in, and prompt for the remaining stuff. */
225251881Speterstatic svn_error_t *
226251881Speterusername_prompt_first_creds(void **credentials_p,
227251881Speter                            void **iter_baton,
228251881Speter                            void *provider_baton,
229251881Speter                            apr_hash_t *parameters,
230251881Speter                            const char *realmstring,
231251881Speter                            apr_pool_t *pool)
232251881Speter{
233251881Speter  username_prompt_provider_baton_t *pb = provider_baton;
234251881Speter  username_prompt_iter_baton_t *ibaton = apr_pcalloc(pool, sizeof(*ibaton));
235251881Speter  const char *no_auth_cache = svn_hash_gets(parameters,
236251881Speter                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
237251881Speter
238251881Speter  SVN_ERR(prompt_for_username_creds
239251881Speter          ((svn_auth_cred_username_t **) credentials_p, pb,
240251881Speter           parameters, realmstring, TRUE, ! no_auth_cache, pool));
241251881Speter
242251881Speter  ibaton->retries = 0;
243251881Speter  *iter_baton = ibaton;
244251881Speter
245251881Speter  return SVN_NO_ERROR;
246251881Speter}
247251881Speter
248251881Speter
249251881Speter/* Subsequent attempts to fetch will ignore the default username
250251881Speter   value, and simply re-prompt for the username, up to a maximum of
251251881Speter   ib->pb->retry_limit. */
252251881Speterstatic svn_error_t *
253251881Speterusername_prompt_next_creds(void **credentials_p,
254251881Speter                           void *iter_baton,
255251881Speter                           void *provider_baton,
256251881Speter                           apr_hash_t *parameters,
257251881Speter                           const char *realmstring,
258251881Speter                           apr_pool_t *pool)
259251881Speter{
260251881Speter  username_prompt_iter_baton_t *ib = iter_baton;
261251881Speter  username_prompt_provider_baton_t *pb = provider_baton;
262251881Speter  const char *no_auth_cache = svn_hash_gets(parameters,
263251881Speter                                            SVN_AUTH_PARAM_NO_AUTH_CACHE);
264251881Speter
265251881Speter  if ((pb->retry_limit >= 0) && (ib->retries >= pb->retry_limit))
266251881Speter    {
267251881Speter      /* give up, go on to next provider. */
268251881Speter      *credentials_p = NULL;
269251881Speter      return SVN_NO_ERROR;
270251881Speter    }
271251881Speter  ib->retries++;
272251881Speter
273251881Speter  return prompt_for_username_creds
274251881Speter         ((svn_auth_cred_username_t **) credentials_p, pb,
275251881Speter          parameters, realmstring, FALSE, ! no_auth_cache, pool);
276251881Speter}
277251881Speter
278251881Speter
279251881Speterstatic const svn_auth_provider_t username_prompt_provider = {
280251881Speter  SVN_AUTH_CRED_USERNAME,
281251881Speter  username_prompt_first_creds,
282251881Speter  username_prompt_next_creds,
283251881Speter  NULL,
284251881Speter};
285251881Speter
286251881Speter
287251881Speter/* Public API */
288251881Spetervoid
289251881Spetersvn_auth_get_username_prompt_provider
290251881Speter  (svn_auth_provider_object_t **provider,
291251881Speter   svn_auth_username_prompt_func_t prompt_func,
292251881Speter   void *prompt_baton,
293251881Speter   int retry_limit,
294251881Speter   apr_pool_t *pool)
295251881Speter{
296251881Speter  svn_auth_provider_object_t *po = apr_pcalloc(pool, sizeof(*po));
297251881Speter  username_prompt_provider_baton_t *pb = apr_pcalloc(pool, sizeof(*pb));
298251881Speter
299251881Speter  pb->prompt_func = prompt_func;
300251881Speter  pb->prompt_baton = prompt_baton;
301251881Speter  pb->retry_limit = retry_limit;
302251881Speter
303251881Speter  po->vtable = &username_prompt_provider;
304251881Speter  po->provider_baton = pb;
305251881Speter  *provider = po;
306251881Speter}
307