Comments (22)
This is now released in version 2.1.1. Please note that it can take up to 24 hours for the new version to show in Jenkins update centre.
from zulip-plugin.
Just tried it. It works. All good now.
Thanks a lot !
from zulip-plugin.
@eXtrem0us Version 2 sends a basic authorization header on every request to authenticate with the Zulip APIs same as previous versions.
Any chance you have a proxy requiring authentication configured in Jenkins settings, but did not provide the credentials?
from zulip-plugin.
Hi
I just upgraded to zulip plugin version 2 and I have experienced exactly the same issue and stack trace.
My proxy settings are ok as I have been able to update properly plugins using that proxy.
I think my zulip credentials are ok.
context :
- jenkins 2.397
- zulip plugin 2.0.0
-update
I updated to jenkins 2.417. Same issue.
Unfortunately, I can't recreate a jenkins instance from scratch as there are other people using/working on it.
from zulip-plugin.
The only way I was able to reproduce the error was when I had a proxy requiring authentication configured in the Jenkins settings, but I did not provide the proxy user:
Proxy config | Proxy error |
---|---|
I have released a new version 2.1.0 with more verbose logging around authentication. If you could please update to it once it hits the update centre and then:
- Configure verbose logging for Zulip in your Jenkins instance
- Try to run a Jenkins job that sends or notifies to Zulip chat.
- In an ideal scenario, you should see something like this:
Since this is not working for you you'll see something else, please post this something else here. Thanks!
from zulip-plugin.
Hi @butchyyyy
I set up the logs and I had the following messages :
Our zulip is on the intranet and is an exception in the jenkins proxy host list.
from zulip-plugin.
Thanks for providing the logs @ebonnet31! In your case, the proxy is not involved due to no proxy host exclusion. Instead, it's the Zulip server returning 401/requesting authentication. The plugin does include credentials (basic authorization) on each Zulip API request so it means the credentials were not valid/rejected by the server.
I can reproduce this locally by entering the wrong credentials into my Jenkins instance:
Can you please double-check your global Jenkins configuration and verify that the Zulip User Email
and Zulip API Key
are correct / for the enabled Zulip bot?
I'll look into adjusting the error handling so it's more obvious what the problem is.
from zulip-plugin.
Hi @butchyyyy
I have tried to reset the mail + api key.
To be sure of the parameters, I tried the parameters using a curl from the zulip example
# For stream messages
curl -X POST https://yourZulipDomain.zulipchat.com/api/v1/messages \
-u BOT_EMAIL_ADDRESS:BOT_API_KEY \
--data-urlencode type=stream \
--data-urlencode 'to="Denmark"' \
--data-urlencode topic=Castle \
--data-urlencode 'content=I come not, friends, to steal away your hearts.'
The curl works with the same apiKey/mail set in jenkins zulip system properties.
But I still have the same jenkins/zulip error.
from zulip-plugin.
maybe an additional suggestion : provide a "test" button in the jenkins system configuration to check that the parameters are ok.
from zulip-plugin.
Additional info : at one point, I entered a wrong url (http instead of https) to see the changes. I had a 301. However, I could see in the log the message :
We sent:api-key=XXXX&subject=AAAA&to=TTTT&type=stream&email=toto%40toto.com&content=Started+build+%2318+of+project+test-zulip
XXX/AAA/TTT/email being correct.
-> seems to show parameters are ok.
I entered back the right url => same exception message.
from zulip-plugin.
@ebonnet31 Thanks for verifying the credentials are correct and for providing all of the additional info. It looks like encoding the credentials into the basic authorization header does not work as expected in your case.
I have tried a bunch of things but had no luck reproducing 😞
Any idea on what could be different in your environment? e.g.:
- Special characters in the bot name / access key
- Charset the Jenkins JVM runs with
- ...
from zulip-plugin.
Hi @butchyyyy
jenkins run on windows with a default charset CP-1252. I have no idea for the the zulip server.
I noticed the line :
As a trial what do you suggest ? I can try to change my jenkins options to have a UTF8 charset for the VM. I'll be able to try that tomorrow.
from zulip-plugin.
I run into the exact problem.
I tried to reform the Zulip class to a standlone one (by removing dependencies for jenkins) to run with Java on local command line, and got the same error. But with a different approach I succeeded to send message to zulip server.
- First I tried this:
java.exe .\Zulip.java
// Zulip.java
import java.io.IOException;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Sends message to Zulip stream
*/
public class Zulip {
private static final Charset encodingCharset = StandardCharsets.UTF_8;
private String url;
private String email;
private String apiKey;
private static Logger LOGGER = Logger.getLogger("");
public Zulip(String url, String email, String apiKey) {
super();
if (url != null && url.length() > 0 && !url.endsWith("/")) {
url = url + "/";
}
this.url = url;
this.email = email;
this.apiKey = apiKey;
}
protected void configureAuthenticator(HttpClient.Builder httpClientBuilder) {
httpClientBuilder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
LOGGER.log(Level.FINE, "{0} authentication requested from {1}:{2}", new Object[] { getRequestorType(),
getRequestingHost(), getRequestingPort() });
switch (getRequestorType()) {
default:
LOGGER.log(Level.FINE, "Unsupported authentication request");
return null;
}
}
});
}
protected HttpClient getClient() throws MalformedURLException {
HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
configureAuthenticator(httpClientBuilder);
return httpClientBuilder.build();
}
protected URI getApiEndpoint(String method) {
StringBuilder uri = new StringBuilder();
if (this.url.length() > 0) {
uri.append(this.url);
uri.append("api/v1/");
} else {
uri.append("https://api.zulip.com/v1/");
}
uri.append(method);
return URI.create(uri.toString());
}
public String getApiKey() {
return this.apiKey;
}
public String getEmail() {
return this.email;
}
public HttpResponse<String> post(String method, Map<String, String> parameters) {
try {
String body = parameters.entrySet()
.stream()
.map(e -> encodeValue(e))
.collect(Collectors.joining("&"));
System.out.println(body);
String auth_info = this.getEmail() + ":" + this.getApiKey();
String encoded_auth = Base64.getEncoder().encodeToString(auth_info.getBytes(encodingCharset));
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(getApiEndpoint(method))
.header("User-Agent", "ZulipJenkins/0.1.2")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic " + encoded_auth)
.POST(HttpRequest.BodyPublishers.ofString(body, encodingCharset))
.build();
HttpClient client = getClient();
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() != 200) {
LOGGER.log(Level.SEVERE,
"Error sending Zulip message:\nStatus:" + httpResponse.statusCode() + "\nBody:"
+ httpResponse.body() + "\n\n" +
"We sent:" + body);
}
return httpResponse;
} catch (IOException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Error sending Zulip message: ", e);
}
return null;
}
public HttpResponse<String> sendStreamMessage(String stream, String subject, String message) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("api-key", this.getApiKey());
parameters.put("email", this.getEmail());
parameters.put("type", "stream");
parameters.put("to", stream);
parameters.put("subject", subject);
parameters.put("content", message);
return post("messages", parameters);
}
private String encodeValue(Map.Entry<String, String> value) {
String toEncode = value.getValue() != null ? value.getValue() : "";
String encodedValue = URLEncoder.encode(toEncode, encodingCharset);
return value.getKey() + "=" + encodedValue;
}
public static void main(String[] args) {
LOGGER.setLevel(Level.SEVERE);
String token = "EEfIvlMKhrWHLCK4UTRNM1U7lUlxnxw5";
Zulip zulip = new Zulip("https://zulip.fw.trd", "[email protected]", token);
zulip.sendStreamMessage("jenkins", "subject", "message");
}
}
- Second, I tried this and succeeded. I can't tell the difference between the two method due to I am not familar with Java. Maybe you can find it out and solve the problem.
java .\ZulipBotMessageSender.java
// ZulipBotMessageSender.java
import java.io.IOException;
import java.net.Authenticator;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Sends message to Zulip stream
*/
public class Zulip {
private static final Charset encodingCharset = StandardCharsets.UTF_8;
private String url;
private String email;
private String apiKey;
private static Logger LOGGER = Logger.getLogger("");
public Zulip(String url, String email, String apiKey) {
super();
if (url != null && url.length() > 0 && !url.endsWith("/")) {
url = url + "/";
}
this.url = url;
this.email = email;
this.apiKey = apiKey;
}
protected void configureAuthenticator(HttpClient.Builder httpClientBuilder) {
httpClientBuilder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
LOGGER.log(Level.FINE, "{0} authentication requested from {1}:{2}", new Object[] { getRequestorType(),
getRequestingHost(), getRequestingPort() });
switch (getRequestorType()) {
default:
LOGGER.log(Level.FINE, "Unsupported authentication request");
return null;
}
}
});
}
protected HttpClient getClient() throws MalformedURLException {
HttpClient.Builder httpClientBuilder = HttpClient.newBuilder();
configureAuthenticator(httpClientBuilder);
return httpClientBuilder.build();
}
protected URI getApiEndpoint(String method) {
StringBuilder uri = new StringBuilder();
if (this.url.length() > 0) {
uri.append(this.url);
uri.append("api/v1/");
} else {
uri.append("https://api.zulip.com/v1/");
}
uri.append(method);
return URI.create(uri.toString());
}
public String getApiKey() {
return this.apiKey;
}
public String getEmail() {
return this.email;
}
public HttpResponse<String> post(String method, Map<String, String> parameters) {
try {
String body = parameters.entrySet()
.stream()
.map(e -> encodeValue(e))
.collect(Collectors.joining("&"));
System.out.println(body);
String auth_info = this.getEmail() + ":" + this.getApiKey();
String encoded_auth = Base64.getEncoder().encodeToString(auth_info.getBytes(encodingCharset));
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(getApiEndpoint(method))
.header("User-Agent", "ZulipJenkins/0.1.2")
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Basic " + encoded_auth)
.POST(HttpRequest.BodyPublishers.ofString(body, encodingCharset))
.build();
HttpClient client = getClient();
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
if (httpResponse.statusCode() != 200) {
LOGGER.log(Level.SEVERE,
"Error sending Zulip message:\nStatus:" + httpResponse.statusCode() + "\nBody:"
+ httpResponse.body() + "\n\n" +
"We sent:" + body);
}
return httpResponse;
} catch (IOException | InterruptedException e) {
LOGGER.log(Level.SEVERE, "Error sending Zulip message: ", e);
}
return null;
}
public HttpResponse<String> sendStreamMessage(String stream, String subject, String message) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("api-key", this.getApiKey());
parameters.put("email", this.getEmail());
parameters.put("type", "stream");
parameters.put("to", stream);
parameters.put("subject", subject);
parameters.put("content", message);
return post("messages", parameters);
}
private String encodeValue(Map.Entry<String, String> value) {
String toEncode = value.getValue() != null ? value.getValue() : "";
String encodedValue = URLEncoder.encode(toEncode, encodingCharset);
return value.getKey() + "=" + encodedValue;
}
public static void main(String[] args) {
LOGGER.setLevel(Level.SEVERE);
String token = "EEfIvlMKhrWHLCK4UTRNM1U7lUlxnxw5";
Zulip zulip = new Zulip("https://zulip.fw.trd", "[email protected]", token);
zulip.sendStreamMessage("jenkins", "subject", "message");
}
}
from zulip-plugin.
I finally found out that the problem had something to do with below line:
configureAuthenticator(httpClientBuilder);
When I commented this line out, then the message got sent. Maybe current implemention is not suitable for deployment of Jenkins without proxy.
from zulip-plugin.
As a trial what do you suggest ? I can try to change my jenkins options to have a UTF8 charset for the VM. I'll be able to try that tomorrow.
That would be great. My test Jenkins instance runs with UTF-8
so I'll try to run it with CP-1252
and see if I can repro (won't manage before tomorrow as well).
from zulip-plugin.
I finally found out that the problem had something to do with below line:
configureAuthenticator(httpClientBuilder);
When I commented this line out, then the message got sent. Maybe current implemention is not suitable for deployment of Jenkins without proxy.
The authenticator should only be invoked when the server requests authentication (401 from Zulip server or 407 from proxy). My expectation would be that with authenticator removed, it will not fail with java.io.IOException: No credentials provided
, but the message will still fail to send.
Removing the authenticator and sending the Proxy-Authorization
header preemptively (if configured in Jenkins) is an option, but I'd like to get to the bottom of the problem before making changes like this.
from zulip-plugin.
I checked return status code from Zulip with curl but found none except 200
.
curl -X POST https://zulip.fw.trd/api/v1/messages -u [email protected]:EEfIvlMKhrWHLCK4UTRNM1U7lUlxnxw5 --data-urlencode type=stream --data-urlencode 'to=jenkins' --data-urlencode topic=OpenBMC --data-urlencode 'content=OpenBMC Build #199 SUCCEED!' -H "User-Agent: ZulipJenkins/0.1.2" -iv
Then I add a
System.out.println("in getPasswordAuthentication");
in method getPasswordAuthentication()
and the message got printed. So below assumption was not true.
The authenticator should only be invoked when the server requests authentication (401 from Zulip server or 407 from proxy).
from zulip-plugin.
I wonder if the root cause is mixing two authentication methods: header based and Authenticator based. The Authenticator is used but SERVER type is not handled. The header based authentication is ignored.
The code below works.
protected void configureAuthenticator(HttpClient.Builder httpClientBuilder) {
httpClientBuilder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
System.out.println("in getPasswordAuthentication");
LOGGER.log(Level.FINE, "{0} authentication requested from {1}:{2}", new Object[] { getRequestorType(),
getRequestingHost(), getRequestingPort() });
switch (getRequestorType()) {
// case PROXY:
// ProxyConfiguration proxyConfiguration = Jenkins.get().proxy;
// if (ZulipUtil.isValueSet(proxyConfiguration.getUserName())) {
// LOGGER.log(Level.FINE, "Using proxy authentication username: {0}, password: ******",
// proxyConfiguration.getUserName());
// return new PasswordAuthentication(proxyConfiguration.getUserName(),
// Secret.toString(proxyConfiguration.getSecretPassword()).toCharArray());
// }
// LOGGER.log(Level.FINE, "Proxy authentication not configured in Jenkins");
// return null;
case SERVER:
LOGGER.log(Level.FINE, "SERVER authentication:");
return new PasswordAuthentication(email, apiKey.toCharArray());
default:
LOGGER.log(Level.FINE, "Unsupported authentication request");
return null;
}
}
});
}
from zulip-plugin.
Hi @butchyyyy
I have tried the UTF-8 encoding
set JENKINS_OPTS=-Dfile.encoding=UTF-8 -Djdk.http.auth.tunneling.disabledSchemes=""
Reset the parameters, so that file would be saved.
upgraded jenkins to the last version and updated plugins.
I still have the same error.
from zulip-plugin.
So... I'm mostly testing locally with Java 11 to make sure the plugin works with the minimal required Java version. I have now switched to Java 17 and I can reproduce the issue there...
I always considered Java to be good in backwards compatibility so I'm flabbergasted this is behaving differently in between versions 😞
The original idea of directly setting the header was to avoid an extra round-trip to the Zulip server, but since this is behaving inconsistently between Java versions, I'm gonna leave the auth headers in the sole discretion of the Authenticator
.
I'll release 2.1.1 version today with a fix.
from zulip-plugin.
I didn't had suspicion on the JDK, but yes I am on the JDK 17 too.
from zulip-plugin.
Finally it works!
Thank you @butchyyyy !
from zulip-plugin.
Related Issues (20)
- build of zulip-plugin fails because of deprecated method getTestResultAction HOT 3
- Post-Build-Action not available - Zulip Notification HOT 7
- Enable Smart Notification is always selected HOT 2
- Plugin Version 1.0.1 does not sent any notifications to Zulip HOT 6
- Environment variables are not resolved HOT 2
- Project missing LICENSE file. HOT 1
- Feature request: possibility to configure smart notifications per Jenkins job. HOT 3
- Zulip notifications in one job are blocked by the other job HOT 1
- FYI, Zulip bot for run jenkins job via trigger url
- Failed builds should use :cross_mark: rather than :x: HOT 5
- send notification if build status changes from before. HOT 1
- Project name is not very clear in notifications for multi-branch pipeline projects
- After upgrading Jenkins, the plugin dosen't work anymore HOT 20
- Does not work with Jenkins 2.387.1 HOT 1
- In a multi-configuration project with an agent axis, the default topic is the name of the agent, not the name of the project/job. HOT 8
- Add send test message button into plugin configuration
- java.lang.NoClassDefFoundError: HOT 2
- Jenkins Pipepline syntax HOT 4
- Blank project topic will override topic from global configuration
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from zulip-plugin.