--- date: "2018-06-24:00:00+02:00" title: "API Usage" slug: "api-usage" weight: 40 toc: false draft: false menu: sidebar: parent: "developers" name: "API Usage" weight: 40 identifier: "api-usage" --- # API Usage **Table of Contents** {{< toc >}} ## Enabling/configuring API access By default, `ENABLE_SWAGGER` is true, and `MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) for more information. ## Authentication Gitea supports these methods of API authentication: - HTTP basic authentication - `token=...` parameter in URL query string - `access_token=...` parameter in URL query string - `Authorization: token ...` header in HTTP headers All of these methods accept the same API key token type. You can better understand this by looking at the code -- as of this writing, Gitea parses queries and headers to find the token in [modules/auth/auth.go](https://github.com/go-gitea/gitea/blob/6efdcaed86565c91a3dc77631372a9cc45a58e89/modules/auth/auth.go#L47). ## Generating and listing API tokens A new token can be generated with a `POST` request to `/users/:name/tokens`. Note that `/users/:name/tokens` is a special endpoint and requires you to authenticate using `BasicAuth` and a password, as follows: ```sh $ curl -XPOST -H "Content-Type: application/json" -k -d '{"name":"test"}' -u username:password https://gitea.your.host/api/v1/users//tokens {"id":1,"name":"test","sha1":"9fcb1158165773dd010fca5f0cf7174316c3e37d","token_last_eight":"16c3e37d"} ``` The ``sha1`` (the token) is only returned once and is not stored in plain-text. It will not be displayed when listing tokens with a `GET` request; e.g. ```sh $ curl --request GET --url https://yourusername:password@gitea.your.host/api/v1/users//tokens [{"name":"test","sha1":"","token_last_eight:"........":},{"name":"dev","sha1":"","token_last_eight":"........"}] ``` To use the API with basic authentication with two factor authentication enabled, you'll need to send an additional header that contains the one time password (6 digitrotating token). An example of the header is `X-Gitea-OTP: 123456` where `123456` is where you'd place the code from your authenticator. Here is how the request would look like in curl: ```sh $ curl -H "X-Gitea-OTP: 123456" --request GET --url https://yourusername:yourpassword@gitea.your.host/api/v1/users/yourusername/tokens ``` You can also create an API key token via your Gitea installation's web interface: `Settings | Applications | Generate New Token`. ## OAuth2 Provider Access tokens obtained from Gitea's [OAuth2 provider](https://docs.gitea.io/en-us/oauth2-provider) are accepted by these methods: - `Authorization bearer ...` header in HTTP headers - `token=...` parameter in URL query string - `access_token=...` parameter in URL query string ### More on the `Authorization:` header For historical reasons, Gitea needs the word `token` included before the API key token in an authorization header, like this: ```sh Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675 ``` In a `curl` command, for instance, this would look like: ```sh curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \ -H "accept: application/json" \ -H "Authorization: token 65eaa9c8ef52460d22a93307fe0aee76289dc675" \ -H "Content-Type: application/json" -d "{ \"body\": \"testing\", \"title\": \"test 20\"}" -i ``` As mentioned above, the token used is the same one you would use in the `token=` string in a GET request. ## Pagination The API supports pagination. The `page` and `limit` parameters are used to specify the page number and the number of items per page. As well, the `Link` header is returned with the next, previous, and last page links if there are more than one pages. The `x-total-count` is also returned to indicate the total number of items. ```sh curl -v "http://localhost/api/v1/repos/search?limit=1" ... < link: ; rel="next",; rel="last" ... < x-total-count: 5252 ``` ## API Guide: API Reference guide is auto-generated by swagger and available on: `https://gitea.your.host/api/swagger` or on the [Gitea demo instance](https://try.gitea.io/api/swagger) The OpenAPI document is at: `https://gitea.your.host/swagger.v1.json` ## Sudo The API allows admin users to sudo API requests as another user. Simply add either a `sudo=` parameter or `Sudo:` request header with the username of the user to sudo. ## SDKs - [Official go-sdk](https://gitea.com/gitea/go-sdk) - [more](https://gitea.com/gitea/awesome-gitea#user-content-sdk) 'n23' href='#n23'>23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
/*
 * Copyright 2012 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.wicket.pages;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;

import com.gitblit.Constants.Transport;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.Menu.ParameterMenuItem;
import com.gitblit.models.NavLink;
import com.gitblit.models.NavLink.DropDownPageMenuNavLink;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.GitblitRedirectException;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.BooleanOption;
import com.gitblit.wicket.panels.ChoiceOption;
import com.gitblit.wicket.panels.ProjectRepositoryPanel;
import com.gitblit.wicket.panels.SshKeysPanel;
import com.gitblit.wicket.panels.TextOption;
import com.gitblit.wicket.panels.UserTitlePanel;

public class UserPage extends RootPage {

	List<ProjectModel> projectModels = new ArrayList<ProjectModel>();

	public UserPage() {
		super();
		throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
	}

	public UserPage(PageParameters params) {
		super(params);
		setup(params);
	}

	@Override
	protected boolean reusePageParameters() {
		return true;
	}

	private void setup(PageParameters params) {
		setupPage("", "");
		// check to see if we should display a login message
		boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, true);
		if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
			authenticationError("Please login");
			return;
		}

		String userName = WicketUtils.getUsername(params);
		if (StringUtils.isEmpty(userName)) {
			throw new GitblitRedirectException(GitBlitWebApp.get().getHomePage());
		}

		UserModel user = app().users().getUserModel(userName);
		if (user == null) {
			// construct a temporary user model
			user = new UserModel(userName);
		}


		add(new UserTitlePanel("userTitlePanel", user, user.username));

		UserModel sessionUser = GitBlitWebSession.get().getUser();
		boolean isMyProfile = sessionUser != null && sessionUser.equals(user);

		if (isMyProfile) {
			addPreferences(user);

			if (app().services().isServingSSH()) {
				// show the SSH key management tab
				addSshKeys(user);
			} else {
				// SSH daemon is disabled, hide keys tab
				add(new Label("sshKeysLink").setVisible(false));
				add(new Label("sshKeysTab").setVisible(false));
			}
		} else {
			// visiting user
			add(new Label("preferencesLink").setVisible(false));
			add(new Label("preferencesTab").setVisible(false));

			add(new Label("sshKeysLink").setVisible(false));
			add(new Label("sshKeysTab").setVisible(false));
		}

		List<RepositoryModel> repositories = getRepositories(params);

		Collections.sort(repositories, new Comparator<RepositoryModel>() {
			@Override
			public int compare(RepositoryModel o1, RepositoryModel o2) {
				// reverse-chronological sort
				return o2.lastChange.compareTo(o1.lastChange);
			}
		});

		final ListDataProvider<RepositoryModel> dp = new ListDataProvider<RepositoryModel>(repositories);
		DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("repositoryList", dp) {
			private static final long serialVersionUID = 1L;

			@Override
			public void populateItem(final Item<RepositoryModel> item) {
				final RepositoryModel entry = item.getModelObject();

				ProjectRepositoryPanel row = new ProjectRepositoryPanel("repository",
						getLocalizer(), this, showAdmin, entry, getAccessRestrictions());
				item.add(row);
			}
		};
		add(dataView);
	}

	@Override
	protected void addDropDownMenus(List<NavLink> navLinks) {
		PageParameters params = getPageParameters();

		DropDownPageMenuNavLink menu = new DropDownPageMenuNavLink("gb.filters",
				UserPage.class);
		// preserve time filter option on repository choices
		menu.menuItems.addAll(getRepositoryFilterItems(params));

		// preserve repository filter option on time choices
		menu.menuItems.addAll(getTimeFilterItems(params));

		if (menu.menuItems.size() > 0) {
			// Reset Filter
			menu.menuItems.add(new ParameterMenuItem(getString("gb.reset")));
		}

		navLinks.add(menu);
	}

	private void addPreferences(UserModel user) {
		// add preferences
		Form<Void> prefs = new Form<Void>("prefsForm");

		List<Language> languages = Arrays.asList(
				new Language("Deutsch","de"),
				new Language("English","en"),
				new Language("Español", "es"),
				new Language("Français", "fr"),
				new Language("Italiano", "it"),
				new Language("日本語", "ja"),
				new Language("한국말", "ko"),
				new Language("Nederlands", "nl"),
				new Language("Norsk", "no"),
				new Language("Język Polski", "pl"),
				new Language("Português", "pt_BR"),
				new Language("簡體中文", "zh_CN"),
				new Language("正體中文", "zh_TW"));

		Locale locale = user.getPreferences().getLocale();
		if (locale == null) {
			// user has not specified language preference
			// try server default preference
			String lc = app().settings().getString(Keys.web.forceDefaultLocale, null);
			if (StringUtils.isEmpty(lc)) {
				// server default language is not configured
				// try browser preference
				Locale sessionLocale = GitBlitWebSession.get().getLocale();
				if (sessionLocale != null) {
					locale = sessionLocale;
				}
			} else {

			}
		}

		Language preferredLanguage = null;
		if (locale != null) {
			String localeCode = locale.getLanguage();
			if (!StringUtils.isEmpty(locale.getCountry())) {
				localeCode += "_" + locale.getCountry();
			}

			for (Language language : languages) {
				if (language.code.equals(localeCode)) {
					// language_COUNTRY match
					preferredLanguage = language;
				} else if (preferredLanguage != null && language.code.startsWith(locale.getLanguage())) {
					// language match
					preferredLanguage = language;
				}
			}
		}

		final IModel<String> displayName = Model.of(user.getDisplayName());
		final IModel<String> emailAddress = Model.of(user.emailAddress == null ? "" : user.emailAddress);
		final IModel<Language> language = Model.of(preferredLanguage);
		final IModel<Boolean> emailMeOnMyTicketChanges = Model.of(user.getPreferences().isEmailMeOnMyTicketChanges());
		final IModel<Transport> transport = Model.of(user.getPreferences().getTransport());

		prefs.add(new TextOption("displayName",
				getString("gb.displayName"),
				getString("gb.displayNameDescription"),
				displayName).setVisible(app().authentication().supportsDisplayNameChanges(user)));

		prefs.add(new TextOption("emailAddress",
				getString("gb.emailAddress"),
				getString("gb.emailAddressDescription"),
				emailAddress).setVisible(app().authentication().supportsEmailAddressChanges(user)));

		prefs.add(new ChoiceOption<Language>("language",
				getString("gb.languagePreference"),
				getString("gb.languagePreferenceDescription"),
				language,
				languages));

		prefs.add(new BooleanOption("emailMeOnMyTicketChanges",
				getString("gb.emailMeOnMyTicketChanges"),
				getString("gb.emailMeOnMyTicketChangesDescription"),
				emailMeOnMyTicketChanges).setVisible(app().notifier().isSendingMail()));

		List<Transport> availableTransports = new ArrayList<>();
		if (app().services().isServingSSH()) {
			availableTransports.add(Transport.SSH);
		}
		if (app().services().isServingHTTP()) {
			availableTransports.add(Transport.HTTP);
		}
		if (app().services().isServingHTTPS()) {
			availableTransports.add(Transport.HTTPS);
		}
		if (app().services().isServingGIT()) {
			availableTransports.add(Transport.GIT);
		}

		prefs.add(new ChoiceOption<Transport>("transport",
				getString("gb.transportPreference"),
				getString("gb.transportPreferenceDescription"),
				transport,
				availableTransports));

		prefs.add(new AjaxButton("save") {

			private static final long serialVersionUID = 1L;

			@Override
			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {

				UserModel user = GitBlitWebSession.get().getUser();

				user.displayName = displayName.getObject();
				user.emailAddress = emailAddress.getObject();

				Language lang = language.getObject();
				if (lang != null) {
					user.getPreferences().setLocale(lang.code);
				}

				user.getPreferences().setEmailMeOnMyTicketChanges(emailMeOnMyTicketChanges.getObject());
				user.getPreferences().setTransport(transport.getObject());

				try {
					app().gitblit().reviseUser(user.username, user);

					setRedirect(true);
					setResponsePage(UserPage.class, WicketUtils.newUsernameParameter(user.username));
				} catch (GitBlitException e) {
					// logger.error("Failed to update user " + user.username, e);
					// error(getString("gb.failedToUpdateUser"), false);
				}
			}
		});

		// add the preferences tab
		add(new Fragment("preferencesLink", "preferencesLinkFragment", this).setRenderBodyOnly(true));
		Fragment fragment = new Fragment("preferencesTab", "preferencesTabFragment", this);
		fragment.add(prefs);
		add(fragment.setRenderBodyOnly(true));
	}

	private void addSshKeys(final UserModel user) {
		Fragment keysTab = new Fragment("sshKeysTab", "sshKeysTabFragment", this);
		keysTab.add(new SshKeysPanel("sshKeysPanel", user));

		// add the SSH keys tab
		add(new Fragment("sshKeysLink", "sshKeysLinkFragment", this).setRenderBodyOnly(true));
		add(keysTab.setRenderBodyOnly(true));
	}

	private class Language implements Serializable {

		private static final long serialVersionUID = 1L;

		final String name;
		final String code;

		public Language(String name, String code) {
			this.name = name;
			this.code = code;
		}

		@Override
		public String toString() {
			return name + " (" + code +")";
		}
	}
}