Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add support to create LDAP/External users through API #2506

Open
Lyuven opened this issue Oct 26, 2024 · 0 comments
Open

Feature: Add support to create LDAP/External users through API #2506

Lyuven opened this issue Oct 26, 2024 · 0 comments
Labels

Comments

@Lyuven
Copy link

Lyuven commented Oct 26, 2024

Related to

Web-Backend (APIs)

Impact

nice to have for enterprise usage

Missing Feature

At the moment Semaphore creates LDAP/External users only on that users first [successful] authentication attempt. This causes issues when you want to assign Users to projects, make them Admin or add users to teams - Before this user has been created. The API today enables all these features except creating Users as "External".

By extending the /users addUser api-method to allow creating users with the type "External: true" we can programmatically add AD-based users and assign them to pre-set projects, Teams, configure roles et cetera. This becomes important in our case where we might have 30-40 users divided into several teams that want access to their respective Project. Without this feature every time a new user wants access to Semaphore that user needs to log in and then ask an Admin to adjust their permissions. I would classify this in-between "nice to have for enterprise usage" and "must have for enterprise usage".

There are already such requests open at the moment:
#2240
#1139

Implementation

func addUser(w http.ResponseWriter, r *http.Request) {
	var user db.UserWithPwd
	var userExternal db.User
	var newUser db.User

	bodyBytes, err := io.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	r.Body.Close()

	// Can't use helpers.bind as it consumes r.
	boundInternalUser := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&user) == nil
	boundExternalUser := json.NewDecoder(bytes.NewReader(bodyBytes)).Decode(&userExternal) == nil

	if !boundInternalUser && !boundExternalUser {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	editor := context.Get(r, "user").(*db.User)
	if !editor.Admin {
		log.Warn(editor.Username + " is not permitted to create users")
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	// Check if external is set to true, then create db.User (no pwd) else User is type internal (external: false)
	if boundExternalUser && userExternal.External {
		newUser, err = helpers.Store(r).CreateUserWithoutPassword(userExternal)
	} else if boundInternalUser {
		newUser, err = helpers.Store(r).CreateUser(user)
	}

	if err != nil {
		log.Warn(editor.Username + " is not created: " + err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	helpers.WriteJSON(w, http.StatusCreated, newUser)
}

Design

By checking whether external: true or external: false we can create the user using CreateUserWithoutPassword or CreateUser respectively. It requires a re-write of addUser which I have an example of above.

It is also possible to extend the API itself with something like /ldapusers thus separating the two, that would keep both functions tidier but seems unnecessary.

You're free to re-write this into more reasonable code if you so wish. :)

@Lyuven Lyuven added the feature label Oct 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants
@Lyuven and others